首先确定优先级:()>[]>*,另外数组指针,类比整型指针,表示指向数组的指针;指针数组,类比整型数组,表示元素为指针的数组。
数组指针
int (*p)[n] 首先()优先级高,它是一个指针,指向一个整型数组。n为数组的长度,当p+1时需要跨越n个整型数据的长度,通常用来表示二维数组及二维数组的函数传参。
一维数组赋值给数组指针时,需要取数组地址或对其进行强制转换,另外相当于二维数组中只有一个行元素,p3+1的地址没有意义,*(p3+1)也无法显示。
1 char a[5] = { 'A', 'B', 'C', 'D' }; 2 char(*p3)[5] = &a; 3 char(*p4)[5] = (char (*)[5])a;
二维数组赋值给数组指针时,具体间接访问格式,可以参考上一篇博文指针和数组的关系。
1 # include <stdio.h> 2 int main(void) 3 { 4 int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 5 int i, j; 6 int (*p)[4] = a; //记住这种定义格式 7 for (i=0; i<3; ++i) 8 { 9 for (j=0; j<4; ++j) 10 { 11 printf("%-2d\x20", *(*(p+i)+j)); /*%-2d中, '-'表示左对齐, 如果不写'-'则默认表示右对齐;2表示这个元素输出时占两个空格的空间*/ 12 } 13 printf("\n"); 14 } 15 return 0; 16 }
二维数组元素地址赋值给一维指针,对元素进行访问时需要跨越n个整型数据的长度,即p + i*N +j == &a[i][j]
1 # include <stdio.h> 2 int main(void) 3 { 4 int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 5 int i, j; 6 int *p = &a[0][0]; //把a[0][0]的地址赋给指针变量p 7 for (i=0; i<3; ++i) 8 { 9 for (j=0; j<4; ++j) 10 { 11 printf("%-2d\x20", *(p+i*4+j)); 12 } 13 printf("\n"); 14 } 15 return 0; 16 }
指针数组
int *p[n] 首先[]优先级高,它是一个数组,前面为int*,表示数组的元素为整型指针,也可表示为int **p, 这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量,可以用来存放变量地址。
其中C中处理字符串和链表数据结构中用该数据类型较多,涉及到二级指针时也容易出问题。
1 int a = 1; 2 int b = 2; 3 int *p[2]; 4 p[0] = &a; 5 p[1] = &b; 6 7 printf("%p\n", p[0]); //a的地址 8 printf("%p\n", &a); //a的地址 9 printf("%p\n", p[1]); //b的地址 10 printf("%p\n", &b); //b的地址 11 printf("%d\n", *p[0]); //p[0]表示a的地址,则*p[0]表示a的值 12 printf("%d\n", *p[1]); //p[1]表示b的地址,则*p[1]表示b的值 13 14 15 //将二维数组赋给指针数组 16 int *pp[3]; //一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2],所以要分别赋值 17 int c[3][4]; 18 for (int i = 0; i < 3; i++) 19 pp[i] = c[i];
数组指针是一个指针变量,占有内存中一个指针的存储空间;
指针数组是多个指针变量,以数组的形式存储在内存中,占有多个指针的存储空间。
指向二维数组时,指针数组和数组指针访问数组元素时完全相同,但函数传参时,只能用数组指针,原因参见指针和数组的关系。
int a[2][3] = { { 0, 1, 2 }, { 3, 4, 5 } }; //2行3列的二维整型数组 int(*p)[3]; //数组指针,指向含有3个元素的一维数组 int *q[2]; //指针数组,一个数组内存放2个指针变量 p = a; q[0] = a[0]; q[1] = a[1]; //输出第1行第2列的值 printf("%d\n", a[1][2]); //5 printf("%d\n", *(p[1] + 2)); //5 printf("%d\n", *(*(p + 1) + 2)); //5 printf("%d\n", (*(p + 1))[2]); //5 printf("%d\n", p[1][2]); //5 printf("%d\n", *(q[1] + 2)); //5 printf("%d\n", *(*(q + 1) + 2)); //5 printf("%d\n", (*(q + 1))[2]); //5 printf("%d\n", q[1][2]); //5
一级指针和二级指针
在进行函数指针传参时,编译器总是要为函数的每个参数制作临时副本(即形参为实参的副本),在调用的时候,实参和形参是不同的内存空间,只是,这个内存空间存放的指针指向的是同一块地址 ,所以形参在函数执行中可以访问实参指向的内存空间,但是形参的指向的改变(即形参所指向的内存空间与实参不同)并不能影响实参,但改变形参所指向的空间的值时(改变形参指向的内存空间的值),实参指向会改变。
一级指针的作用:在函数外部定义一个值m,在函数内对m进行赋值改变,函数结束后对值m生效。一级指针不管形参、实参都指向值m的地址,对一级指针进行解引用时都是访问m的地址空间,然后访问m,因此若改变形参指向的空间的值(即改变了m的值),则实参也会改变。
二级指针作用:在函数外部定义一个指针p,在函数内给指针赋值,函数结束后对指针p生效。二级指针不管形参、实参都指向指针p,对二级指针进行解引用时都是先访问指针p的地址空间,再访问指针p,因此若改变形参指向的空间的值(即改变了指针p的值),则实参也会改变。
如果用了二级指针,但不想改变外部的指针p,可以在函数内部对二级指针指向的空间的值做一个拷贝。
1 void my_malloc(char **s) 2 { 3 *s=(char*)malloc(100); 4 } 5 6 void main() 7 { 8 char *p=NULL; 9 my_malloc(&p); 10 //do something 11 if(p) 12 free(p); 13 }
这里给指针p分配内存,do something,然后free(p),如果用一级指针,那么就相当于给一个p的拷贝s分配内存,p依然没分配内存,用二级指针之后,才对p分配了内存。
应用上面的二级指针的例子
1 void getstring_(char **p){ 2 //char *p; 3 *p = malloc(100); 4 memset(*p, 0, 100); 5 strcpy(*p, "hello"); 6 } 7 8 void test_(){ 9 char *ret = NULL; 10 getstring_(&ret); 11 printf("%s\n", ret); 12 } 13 14 int main(){ 15 test_(); 16 17 system("pause"); 18 return 0; 19 }