一道指针题

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;

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值