C语言一维/二维数组解引用难理解点以及一道难题

C语言指针相关的坑爹题


先来一点简单的

求下面各代码打印结果(32位环境):

int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));

我们首先要明白

  1. 数组名单独放在sizeof()内部,计算的是整个数组大小。
  2. 如果&数组名放在里面就是求指针大小
  3. 二维数组中,数组名是首元素地址,二维数组的首元素是第一行的地址,对它解引用就是第一行第一个元素的地址,再解引用一次就是第一行第一个元素的值。

我们再用一维数组仔细解释一下第三条的原因:

给定一个一维数组 a[4]={1,2,3,4}
那么数组名a就代表该一维数组的首元素地址,即1的地址,对它解引用就是1;

但我们再看二维数组
给定一个二维数组 a[2][2]={1,2,3,4}
在这,数组名也是首元素地址
但二维数组的首元素地址代表着第一行整体的地址,a+1就是第二行的地址。
如果对a解引用*a,*a就是第一行第一个元素的地址,*a+1就是第一行第二个元素的地址
如果再解引用一次,**a就是第一行第个元素的值。


可见,二维数组解引用一次,就变成了一维数组,再用一维数组的思维去理解是不是就更容易了一点。

接下来对上面的题解析:

int a[3][4] = {0};
printf("%d\n",sizeof(a));/数组名单独防在sizeof里面,求长度,所以结果是3*4*4=48

printf("%d\n",sizeof(a[0][0]));/a的第一行第一个元素是0,类型为int,结果就为4

printf("%d\n",sizeof(a[0]));/a[0]代表二维数组第一个元素,第一个元素是它的第一行整个数组的地址,
也就相当于一个一维数组的数组名,单独放在sizeof里为求它大小,为16

printf("%d\n",sizeof(a[0]+1));a[0]作为第一行数组名,没有单独放在sizeof内部,a表示第一行地址
那么a+1就是第二行地址,是地址,所以大小为4

printf("%d\n",sizeof(*(a[0]+1)));/a[0]+1是第二行数组首元素的地址,对它解引用就是0,类型为int
大小为4

printf("%d\n",sizeof(a+1));/a数组名没有单独放在sizeof内部,没有&,所以a为第一行整体的地址,a+1
就是第二行整体的地址,类型为int(*)[4]的数组指针,32位环境下结果为4printf("%d\n",sizeof(*(a+1)));/同上,对数组指针解引用,a+1是整个第二行数组的整体地址,对它进行
第一次解引用就是第二行首元素的地址,又因为数组名等于首元素地址,所以在这*(a+1)相当于一维数组的数组
名,所以为求整个第二行数组的大小,结果为16

printf("%d\n",sizeof(&a[0]+1));/a[0]为第一行首元素的地址,对它取地址就相当于二维数组a首元素的
地址,二维数组首元素地址是第一行整体的地址,加一就是第二行整体的地址,所以&a[0]+1 -> a+1 ,是地
址,所以大小为4

printf("%d\n",sizeof(*(&a[0]+1)));/同上,对a+1解引用就是第二行第一个元素的地址,相当于一维数组
的数组名单独放在sizeof里,求第二行数组的大小,为16

printf("%d\n",sizeof(*a));/a是二维数组的数组名,也就是首元素的地址,,就是第一行第一个元素的地址
相当于一维数组名单独放在sizeof里,求第一行数组的大小,为16

printf("%d\n",sizeof(a[3]));/a[3]确实越界了,但是sizeof里面的表达式不进行实际运算,所以还是直
接看着第四行的数组名,求数组大小,为16

接下来是一道难题:
int main() 
{
    int a[4] = { 1, 2, 3, 4 };
    int *ptr1 = (int *)(&a + 1);
    int *ptr2 = (int *)((int)a + 1); 
    printf( "%x,%x", ptr1[-1], *ptr2); 
    return 0;
}

求结果
解析:ptr1很简单:&a是整个一维数组的地址,加一就跳过了整个a数组,指向4后面的一个地址。

打印的时候ptr1[-1]相当于*(ptr1-1),结果就是4。
再来看ptr2:a是数组a的首元素地址,强制转化为int类型,就变成了普通的整型,整型加一就直接看成一个数加一,就相当于只跳过一个字节,不管是跳过一个int还是一个char。
a数组在内存中以2进制方式存储,并且,我们在这默认为小段存储。
10 00 00 00 20 00 00 00 30 00 00 00 40 00 00 00
一开始a指向数组首元素,ptr2变成整型加一后,再强制类型转换为int*,就相当于只跳过一个字节,指向了二进制位01后面一个地址,再以十六进制形式打印,从00往后找四个字节,这时找到的就是00 00 00 02,打印出来的是2。
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值