什么是指针?
从根本上看指针是一个值为内存地址的变量(或数据对象),指针变量的值是地址。
与指针相关的运算符
- & 后接一个变量名时,&给出该变量的地址
- * 后跟一个指针名或者地址时,*给出存储在指针指向地址上的值
声明指针变量时必须指定指针所指向变量的类型,因为不同的变量类型占用不同的存储空间,一些指针的操作要求知道操作对象的大小(例如指针的加法、减法等都是以指向对象为准的)。另外,程序需要知道存储在指定地址上的数据类型。
指针的初始化:
//在指针定义处对指针进行初始化操作 #include指针的基本操作int main(void) { int a = 10; //此处的*p和定义后的*p代表的意义不同 int *p = &a; } //先定义指针后初始化 #include int main(void) { int a = 10; int *p; p=&a; }
- 赋值操作:可以将地址赋给指针。例如,用数组名、带地址运算符(&)的变量名、另一个指针进行赋值。注意:地址应该和指针类型兼容。也就是说,不能把double类型的地址赋给指向int的指针。
double i=10; int *p; p=&i //避免这样做
- 解引用:*运算符给出指针指向地址上存储的值。注意:不能解引用一个未初始化的指针。创建一个指针,系统只分配了存储指针本身的内存,并未分配存储数据的内存。因此,在使用指针之前,必须先用已分配的地址初始化它。
int i=3,*p; *p=i;//不要这样做,因为指针p没有进行初始化
- 取址:和所有变量一样,指针变量也有自己的地址和值。对于指针而言,&运算符给出指针本身的地址。
- 指针和整数相加:可以使用+运算符把指针与整数相加,或整数与指针相加。无论哪种情况,整数都会和指针所指向类型的大小(以字节为单位)相乘,然后把结果与初始地址相加。
- 递增指针:递增指向数组元素的指针可以让指针移动至数组的下一个元素
- 指针减去一个整数:可以使用-运算符从一个指针中减去一个整数,指针必须是第一个运算对象,整数是第二个运算对象。如果相减的结果超过了初始指针所指向数组的范围,计算结果则是未定义的。除非正好超过数组末尾第一个位置,C保证该指针有效
- 递减指针
- 指针求差:通常,求差的两个指针分别指向同一个数组的不同元素,通过计算求出两元素之间的距离。差值的单位与数组类型的单位相同。如果指向两个不同数组的指针进行求差运算可能会得到一个值,或者导致运行时错误。
- 比较:使用关系运算符可以比较两个指针的值,前提是两个指针指向相同的数据类型的对象。
在C语言中,如果一个指针不指向任何数据,我们称之为空指针,使用NULL表示。因为空指针没有指向任何数据,所以地址就是随机值。一些函数对空指针是很敏感的(如结构体中的strcpy_s()函数),在使用前判断一下是否为空。
指针和数组为什么要将指针和数据结合使用?
指针能够有效的处理数组,数组表示法其实是在变相的使用指针。
一维数组和指针数组名是数组首元素的地址,也就是说如果flizny是一个数组,那么下列的语句成立:
flizny == &flizny[0]
指针表示法和数组表示法
在C语言中,a[i]和*(a+i)这两个表达式都是等价的。无论a是数组名还是指针变量,这两个表达式都没有问题。但是,只有当a是指针变量时,才能够使用a++这样的表达式。
保护数组中的数据
防止在使用指针的过程中对数组中的元素进行了更改我们需要使用const来保护数组中的数据不被更改。主要有三种方式来保证相关的数据不被更改。
- const int *p 不改变地址上的值
- int * const p 不改变指针p
- const int * const p 不改变地址上的值,也不改变指针p
创建一个二维的数组:
int zippo[4][2]//四行两列的数组
zippo的首元素是一个内含两个int值的数组,数组名zippo是该数组首元素的地址。
#include字符串int sum_demo1(int* a, int n); int sum_demo2(int* start, int* end); void sum_rows(int ar[][2], int rows);//二维数组行和函数 void sum_cols(int(*p)[4], int rows);//二维数组列和函数 void sum_total(int p[][2], int rows);//二维数组总和函数 int main(void) { //创建一维数组 int b[5] = { 1,2,3,4,5 }; int a[] = { 1,2,3,4 }; //省略方框中的数字,让编辑器自动匹配 int sum_b = 0; int sum_a = 0; //数组的引用 数组名[下标]、*(b+1)与数组名表达形式相同 for (int i = 0; i < 5; i++) { printf("b[%d]=%dn", i, b[i]); } for (int i = 0; i < 5; i++) { printf("b=[%d]=%dn", i, *(b + i)); } //数组名 printf("数组名表示首字母地址,a=%pn", a); printf("数组名的地址一样是首字母地址,&a=%pn", &a); //在函数中调用数组 //因为作为实际参数的数组名是一个指针,所以要求形参是一个与之匹配的形参 sum_b = sum_demo1(b, 5); printf("sum_b=%dn", sum_b); sum_a = sum_demo2(a, (a + 4)); printf("sum_a=%dn", sum_a); //创建二维数组 int c[3][2] = { {1,2},{3,4},{5,6} }; //数组名 printf("c表示{1,2}地址,c[0]表示{1,2}中1的地址"); printf("c的地址,&c=%pn", &c); printf("数组名表示首元素地址即{1,2}地址,c=%pn", c); printf("c[0]=%pn", c[0]); printf("数组名表示首个整数地址即{1,2}中{1}的地址,c[0][0]=%pn", &c[0][0]); //数组的引用 for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { printf("c[%d][%d]=%dn", i, j, c[i][j]); printf("c[%d][%d]=%dn", i, j, *(c[i] + j)); printf("c[%d][%d]=%dn", i, j, *((*(c + i)) + j)); } } sum_rows(c, 3); sum_cols(c, 3); sum_total(c, 3); } //一维数组相加函数 int sum_demo1(int* a, int n) { int i; int total = 0; for (i = 0; i < n; i++) { //在函数中使用数组表示法,使得意图更加明显 total += a[i]; } return total; } //使用指针算法 int sum_demo2(int* start, int* end) { int total = 0; while (start < end) { total += *start; start++; } return total; } //二维数组行和函数 //第一个括号是空的,空的括号表示ar是一个指针 void sum_rows(int ar[][2], int rows) { int rows_total = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j < 2; j++) { rows_total += ar[i][j]; } printf("rows_total=%dn", rows_total); rows_total = 0; } } //列和函数 //int(*p)[4]定义一个p指针,指向一个含有四个int类型值的一维数组 void sum_cols(int(*p)[2], int rows) { int cols_total = 0; for (int i = 0; i < 2; i++) { for (int j = 0; j < rows; j++) { cols_total += p[j][i]; } printf("cols_total=%dn", cols_total); cols_total = 0; } } //二维数组总和函数 void sum_total(int p[][2], int rows) { int total = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j < 2; j++) { total += *(*(p + i) + j); } } printf("total=%dn", total); }
| 函数名 | 作用 | 缺点 |
|---|---|---|
| printf() | 将字符串的地址作为参数,依次打印直到结束。 | |
| puts() | 显示字符串,并在末尾添加换行符。 | |
| gets() | 读取整行,直至遇到换行符,然后丢弃换行符,存储其他字符,并在这些字符的末尾添加一个空字符。 | gets函数输入参数只有words不能检测数组是否能装下输入行。若字符过长将导致缓冲区溢出。 |
| fgets(words,stlen,stdin) | 大多用于处理文件输入,第二个参数用于指定输入字符的最大数量。如果该参数的值是n那么读入n-1个字符,或者读到遇到的第一个换行符为止。当输入行不溢出是,fgets函数将换行符放在最后。 第三个参数用于说明读入的文件,如果读入从键盘输入的数据,则以stdin作为参数 | |
| fputs(words,stdout) | 第二个参数指定它要写入的文件。 | |
| scanf() | 以一个空白字符(空行、空格、制表符或换行符)作为字符串的结尾 | 不安全可能会产生溢出的情况。 |
| scanf_s("%s",a,30) | scanf()函数的改进,给定需要传入的长度,防止溢出。 | |
| string.h系列函数 | ||
| strlen() | 统计字符串的长度 | |
| strcat(str1,str2) | 拼接字符串,传入两个字符串,将str2副本加到str1后面,并返回str1.str1被改变,str2不变 | str1的大小可能不足以容纳str1和str2产生溢出 |
| strncat(str1,str2,max) | 上述函数的改进,第三个参数规定str2最多多少个字符可以加到str1 | |
| strcmp() | 检测字符串是否相同,如果两个字符串参数相同,该函数返回0. | |
| strncmp(str1,str2,num) | 第三个参数限定函数只查找几个字符。 | |
| strcpy(copy,orig) | 拷贝整个字符串 | |
| strncpy(copy,orig,num) | 指定最大拷贝字符数 | |
#include结构体int max(int, int); int min(int, int); int add(int, int); int main(void) { int (*p)();//指向函数的指针 int a, b, c; p = max; scanf_s("%d %d", &a, &b); c = (*p)(a, b); printf("a=%d,b=%d,max=%dn", a, b, c); //用指向函数的指针作为函数参数 //这样在函数中就可以调用其他函数了 printf("max="); progress(a, b, max); printf("min="); progress(a, b, min); printf("sum="); progress(a, b, add); } int progress(int x, int y, int(*p)()) { int i = 0; if (p == max) { i = (*p)(x, y); printf("%dn",i); } else if (p == min) { i = (*p)(x, y); printf("%dn", i); } else if (p = add) { i = (*p)(x, y); printf("%dn", i); } } int max(int x, int y) { int i = 0; if (x > y) { i = x; } else { i = y; } return i; } int min(int x, int y) { int i = 0; if (x > y) { i = y; } else { i = x; } return i; } int add(int x, int y) { int i = 0; i = x + y; return i; }
#include#include #include #define MAXTITLE 41 #define MAXAUTHOR 31 #define SLEN 81 //声明结构体 struct book { char title[MAXTITLE]; char author[MAXAUTHOR]; float value; }; struct off { float first_off; float seconf_off; }; struct buy { struct off buyit; char name[30]; float value; }; struct namect { char* fname; char* lname; int letters; }; char* s_gets(char* st, int n); void getinfo(struct namect* pst); void makeinfo(struct namect* pst); void showinfo(struct namect* pst); void cleanup(struct namect* pst); int main(void) { //定义结构变量、初始化结构 struct book book1 = { "The C PRIMER","Cking",21.1 }; //访问结构成员 printf("book1.title=%sn", book1.title); printf("book1.author=%sn", book1.author); printf("book1.value=%fn", book1.value); //结构数组 struct book library[2] = { {"The C PRIMER","Cking",21.1},{"The Lunix","C_king",21.1} }; printf("library[0].title=%sn", library[0].title); printf("library[1].title=%sn", library[1].title); //嵌套结构的使用 struct buy nike_buy = { {0.8,0.7},"nike",500 }; printf("初始价格为:%fn", nike_buy.value); printf("折上折后的价格为:%lf", nike_buy.buyit.first_off *nike_buy.buyit.seconf_off*nike_buy.value); //指向结构的指针 struct book* p; struct book* q; p = &book1; printf("book1.title=%sn", (*p).title); printf("book1.author=%sn", (*p).author); printf("book1.value=%fn", (*p).value); q = library; printf("library[0] address=%p,library[2] address=%pn", &library[0], &library[1]); printf("q:%p,q+1:%pn", q, q + 1); printf("library[0].title=%sn",(*q).title ); printf("library[1].title=%sn",(*(q+1)).title ); struct namect person; getinfo(&person); makeinfo(&person); showinfo(&person); cleanup(&person); return 0; } void getinfo(struct namect* pst) { char temp[SLEN]; printf("Please enter your first name.n"); s_gets(temp, SLEN); // 分配内存 (*pst).fname = (char*)malloc(strlen(temp)*sizeof(char)+1); //pst->fname= (char*)malloc(strlen(temp) + 1); //将数据写入分配好的内存中 if ((*pst).fname == NULL) return EOF; else strcpy_s((*pst).fname, strlen(temp)+1, temp); printf("please enter your last name.n"); s_gets(temp, SLEN); (*pst).lname = (char*)malloc(strlen(temp)*sizeof(char) + 1); //pst->fname= (char*)malloc(strlen(temp) + 1); //将数据写入分配好的内存中 strcpy_s((*pst).lname, strlen(temp)+1, temp); } void makeinfo(struct namect* pst) { (*pst).letters = strlen((*pst).fname) + strlen((*pst).lname); } void showinfo(struct namect* pst) { printf("%s %s,your name contins %d letters.n", (*pst).fname, (*pst).lname, (*pst).letters); } void cleanup(struct namect* pst) { free((*pst).fname); free((*pst).lname); } char* s_gets(char* st, int n) { char* ret_val; char* find; ret_val = fgets(st, n, stdin); if (ret_val) { find = strchr(st, 'n'); if (find) { *find = ' '; } else { while (getchar() != 'n') continue; } } return ret_val; }



