int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
乍一看到这串代码,想必是有些蒙的。
想要理解这串代码,不妨将代码逐行分析一下。
int a[5][5];
声明一个五行五列的二维数组
int(*p)[4];
声明一个指针变量p,指针的类型为 int [4]。
即声明了一个数组指针,该数组的元素类型为int 有四个元素。
p = a;
将a的值赋给p。
既然谈到将a的值赋给p,不妨先讨论一下a。
a是二维数组的数组名,在大部分情况下为首元素地址。(存在两种特殊情况,一是单独存在于sizeof()内部时,而是&a。这两种情况代表整个数组)故a表示的为二维数组的首元素地址。
二维数组的首元素是什么呢?
由图不难理解,二维数组的首元素其实是 第一行一维数组。
既然二维数组的首元素为一维数组,不难理解a既然为首元素地址。
那么a应为数组指针,数组的元素类型为 int 有五个元素。
可上文分析的,p也是一个数组指针,但数组的类型有一些差异,是一个整型数组,但只有四个元素。将a赋值给p难免指针类型会发生一些变化。
既然谈到a这个指针,难免需要再谈论一些二维数组。
二维数组中的元素其实在内存中是连续存放的,不妨将二维数组在内存中的存储理解为
数据在内存中存放是连续的,而且数组在内存中是连续存放的。图中两个数组之间的空隙是为了方便区分,并非表示二维数组的每个元素是分开存放的。
综上所述,a指针指向的是一个数组,数组中元素的类型是int ,共有5个元素
即 a 的类型为 int (*) [5],
言归正传,同理分析可知
p 的类型为int (*) [4]
将a赋值给p,p便认为传输过来的地址是一个int (*) [4]的指针
即
printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
这串代码的关键便是 &p[4][2]
就像上文所述,p是一个 int (*) [4]的指针,任何被传给p的数据将被理解为int (*) [4]的指针
分析一下 &p[4][2] 即为 取出 p[4][2]的地址。
众所周知,a[i] 等价于 *(a+i)
故 p[4][2]等价于 *(*(p+4)+2)
先分析 *(p+4)
p是一个指针,指针加一跳过一个元素,指针指向的是一个数组。
故一次跳过一个数组,该数组有四个int类型的元素
p+4指向的数组由图所示
对数组指针解引用的到是整个数组,即数组名。
*(p+4) 即为图中所标记的数组名
为了方便理解假设这个数组的数组名称为 arr
那么 *(p+4) 等价于 arr。
那么 *(*(p+4)+2) 也就为 *(arr+2) 即 arr[2]。
也就是arr数组中下标为2的元素
综上所述,&p[4][2] 所指即为 一个整型元素的地址
&a[4][2]便好理解许多,是一个二维数组中下标为 4 2 元素的地址即
再看回这个代码
&p[4][2] - &a[4][2]
即为两个整型数组想减,所得结果即为 两指针间的元素个数
有一点需要注意,数组再内存中是连续存放的,优先使用低地址。
故 &p[4][2] , &a[4][2]两指针间的元素个数为4,但因为&p[4][2] 的地址小于 &a[4][2] 的地址。
所得应该为-4;
故可以将代码理解为
printf( "%p,%d\n", -4, -4);
-4在内存中存储的是补码
-4的补码为 : 11111111 11111111 11111111 11111100
16进制表示为 : ff ff ff ff fc
%p 是将数据以地址的形式进行打印,将会直接以16进制打印-4的补码
%d 是将数据以整型的形式进行打印,将会直接打印-4;