数组指针也称行指针
定义 int (*p)[n];
()优先级高首先说明p是一个指针指向一个整型的一维数组这个一维数组的长度是n也可以说是p的步长。也就是说执行p1时p要跨过n个整型数据的长度。
如要将二维数组赋给一指针应这样赋值int a[3][4];int (*p)[4]; //该语句是定义一个数组指针指向含4个元素的一维数组。 pa; //将该二维数组的首地址赋给p也就是a[0]或; //该语句执行过后也就是pp1;p跨过行a[0][]指向了行a[1][]所以数组指针也称指向一维数组的指针亦称行指针。指针数组定义 int *p[n];[]优先级高先与p结合成为一个数组再由int*说明这是一个整型指针数组它有n个指针类型的数组元素。这里执行p1时则p指向下一个数组元素这样赋值是错误的pa因为p是个不可知的表示只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *pa; 这里*p表示指针数组第一个元素的值a的首地址的值。如要将二维数组赋给一指针数组:int *p[3];int a[3][4];p; //该语句表示p数组指向下一个数组元素。注此数组每一个元素都是一个指针for(i0;i<3;i)p[i]a[i]这里int *p[3] 表示一个一维数组内存放着三个指针变量分别是p[0]、p[1]、p[2]所以要分别赋值。这样两者的区别就豁然开朗了数组指针只是一个指针变量似乎是C语言里专门用来指向二维数组的它占有内存中一个指针的存储空间。指针数组是多个指针变量以数组形式存在内存当中占有多个指针的存储空间。还需要说明的一点就是同时用来指向二维数组时其引用和用数组名引用都是一样的。比如要表示数组中i行j列一个元素*(p[i]j)、*(*(pi)j)、(*(pi))[j]、p[i][j]优先级()>[]>*
一、指针数组和数组指针的内存布局初学者总是分不出指针数组与数组指针的区别。其实很好理解指针数组首先它是一个数组数组的元素都是指针数组占多少个字节由数组本身的大小决定每一个元素都是一个指针在32 位系统下任何类型的指针永远是占4 个字节。它是“储存指针的数组”的简称。数组指针首先它是一个指针它指向一个数组。在32 位系统下任何类型的指针永远是占4 个字节至于它指向的数组占多少字节不知道具体要看数组大小。它是“指向数组的指针”的简称。下面到底哪个是数组指针哪个是指针数组呢A)int *p1[10];B)int (*p2)[10];每次上课问这个问题总有弄不清楚的。这里需要明白一个符号之间的优先级问题。“[]”的优先级比“*”要高。p1 先与“[]”结合构成一个数组的定义数组名为p1int *修饰的是数组的内容即数组的每个元素。那现在我们清楚这是一个数组其包含10 个指向int 类型数据的指针即指针数组。至于p2 就更好理解了在这里“”的优先级比“[]”高“*”号和p2 构成一个指针的定义指针变量名为p2int 修饰的是数组的内容即数组的每个元素。数组在这里并没有名字是个匿名数组。那现在我们清楚p2 是一个指针它指向一个包含10 个int 类型数据的数组即数组指针。我们可以借助下面的图加深理解
二、int (*)[10] p2-----也许应该这么定义数组指针这里有个有意思的话题值得探讨一下平时我们定义指针不都是在数据类型后面加上指针变量名么这个指针p2 的定义怎么不是按照这个语法来定义的呢也许我们应该这样来定义p2 int (*)[10] p2;int (*)[10]是指针类型p2 是指针变量。这样看起来的确不错不过就是样子有些别扭。其实数组指针的原型确实就是这样子的只不过为了方便与好看把指针变量p2 前移了而已。你私下完全可以这么理解这点。虽然编译器不这么想。^_^三、再论a 和那问题就来了。前面我们讲过a 和现在再来看看下面的代码int main(){ char a[5]{A,B,C,D}; char (*p3)[5] char (*p4)[5] a; return 0;}上面对p3 和p4 的使用哪个正确呢p31 的值会是什么p41 的值又会是什么毫无疑问p3 和p4 都是数组指针指向的是整个数组。a是数组首元素的首地址其值相同但意义不同。在C 语言里赋值符号“”号两边的数据类型必须是相同的如果不同需要显示或隐式的类型转换。p3 这个定义的“”号两边的数据类型完全一致而p4 这个定义的“”号两边的数据类型就不一致了。左边的类型是指向整个数组的指针右边的数据类型是指向单个字符的指针。在Visual C6.0 上给出如下警告 warning C4047: initializing : char (*)[5] differs in levels of indirection from char *。还好这里虽然给出了警告但由于而变量作为右值时编译器只是取变量的值所以运行并没有什么问题。不过我仍然警告你别这么用。既然现在清楚了p3 和p4 都是指向整个数组的那p31 和p41 的值就很好理解了。但是如果修改一下代码把数组大小改小点会有什么问题p31 和p41 的值又是多少呢int main(){ char a[5]{A,B,C,D}; char (*p3)[3] char (*p4)[3] a; return 0;}甚至还可以把代码再修改把数组大小改大点int main(){ char a[5]{A,B,C,D}; char (*p3)[10] char (*p4)[10] a; return 0;}这个时候又会有什么样的问题p31 和p41 的值又是多少上述几个问题希望读者能仔细考虑考虑并且上机测试看看结果。测试结果:(1).char (*p2)[5]a;必须使用强制转换,如:char (*p2)[5](char (*)[5])a;(2).把数组大小改变,都会编译不通过,提示:error C2440: initializing : cannot convert from char (*)[5] to char (*)[3]error C2440: initializing : cannot convert from char (*)[5] to char (*)[10](3).把以上程序测试代码如下:int main(){ char a[5]{a,b,c,d}; char (*p1)[5] char (*p2)[5](char (*)[5])a; printf("a%dn",a); printf("a%cn",a[0]); printf("p1%cn",**p1); printf("p2%cn",**p2); printf("p11%cn",**(p11)); printf("p21%cn",**(p21)); return 0;}输出:a1638208aap1ap2ap11?p21?Press any key to continue结论:根据指针类型及所指对象,表示指针大小,每次加1,表示增加指针类型大小的字节.----后面还会有解释说明.
四、地址的强制转换先看下面这个例子struct Test{ int Num; char *pcName; short sDate; char cha[2]; short sBa[4];}*p;假设p 的值为0x100000。如下表表达式的值分别为多少 p 0x1 0x___ ? (unsigned long)p 0x1 0x___? (unsigned int*)p 0x1 0x___?我相信会有很多人一开始没看明白这个问题是什么意思。其实我们再仔细看看这个知识点似曾相识。一个指针变量与一个整数相加减到底该怎么解析呢还记得前面我们的表达式“a1”与“1”之间的区别吗其实这里也一样。指针变量与一个整数相加减并不是用指针变量里的地址直接加减这个整数。这个整数的单位不是byte 而是元素的个数。所以p 0x1 的值为0x100000sizofTest*0x1。至于此结构体的大小为20byte前面的章节已经详细讲解过。所以p 0x1 的值为0x100014。(unsigned long)p 0x1 的值呢这里涉及到强制转换将指针变量p 保存的值强制转换成无符号的长整型数。任何数值一旦被强制转换其类型就改变了。所以这个表达式其实就是一个无符号的长整型数加上另一个整数。所以其值为0x100001。(unsigned int*)p 0x1 的值呢这里的p 被强制转换成一个指向无符号整型的指针。所以其值为0x100000sizofunsigned int*0x1等于0x100004。上面这个问题似乎还没啥技术含量下面就来个有技术含量的在x86 系统下其值为多少intmain(){ int a[4]{1,2,3,4}; int *ptr1(int *)(1);//指向a数组后面的内存单元1表示向后移16个存储单元 int *ptr2(int *)((int)a1);//表示a的存储单元的地址增加一个字节 printf("%x,%x",ptr1[-1],*ptr2);//ptr1[-1]其实指向的是a数组的最后一个单元*ptr1则表示a数组的地址后移一个字节之后的4个连续存储单元所存储的值 return 0;}这是我讲课时一个学生问我的题他在网上看到的据说难倒了n 个人。我看题之后告诉他这些人肯定不懂汇编一个懂汇编的人这种题实在是小case。下面就来分析分析这个问题根据上面的讲解1 与a1 的区别已经清楚。ptr1将1 的值强制转换成int*类型赋值给int* 类型的变量ptrptr1 肯定指到数组a 的下一个int 类型数据了。ptr1[-1]被解析成*(ptr1-1)即ptr1 往后退4 个byte。所以其值为0x4。ptr2按照上面的讲解(int)a1 的值是元素a[0]的第二个字节的地址。然后把这个地址强制转换成int*类型的值赋给ptr2也就是说*ptr2 的值应该为元素a[0]的第二个字节开始的连续4 个byte 的内容。其内存布局如下图
好问题就来了这连续4 个byte 里到底存了什么东西呢也就是说元素a[0],a[1]里面的值到底怎么存储的。这就涉及到系统的大小端模式了如果懂汇编的话这根本就不是问题。既然不知道当前系统是什么模式那就得想办法测试。大小端模式与测试的方法在第一章讲解union 关键字时已经详细讨论过了请翻到彼处参看这里就不再详述。我们可以用下面这个函数来测试当前系统的模式。int checkSystem(){ union check { int i; char ch; } c; c.i 1; return (c.ch 1);//如果当前系统为大端模式这个函数返回0如果为小端模式函数返回1。}如果当前系统为大端模式这个函数返回0如果为小端模式函数返回1。也就是说如果此函数的返回值为1 的话*ptr2 的值为0x2000000。如果此函数的返回值为0 的话*ptr2 的值为0x100。
【感谢本站数字底座由龙石数据提供 http://www.longshidata.com/pages/government.html】