sizeof与指针步长

开始学c语言的时候早早就接触到了sizeof这个关键字,当时就有一个让我非常困惑的问题,为什么sizeof能够测量出数组的长度,但是用sizeof去测量一个指向数组的指针,却只能得到指针值的长度。

就像这样

    int a[20];
    int *b = a;
    printf("%d\n",sizeof(a));
    printf("%d\n",sizeof(b));

结果:

80
4

这就很奇怪了,大学老师,包括很多的书上都告诉我们数组名是指向数组的首位置的指针,那为什么我sizeof能测量出他的长度,但是把他的值赋值给另外一个指针就不能测量出长度了?我认为数组名就是指向数组首位置的指针这句话可能并不完全正确,或者说它与我理解指针有些不同,他可能是指向数组首位置的指针,但是这个指针的类型,肯定与我们int *b这种指针类型有所不同。

题外话:

数组名是常量地址值,指针是变量指针。

 

关于sizeof的两个精巧的宏实现,其他资料中查找到的,从中我们能知道sizeof为什么能测量出一个数组的长度

//针对T为一个类型名的情况
#defne _sizeof(T) ( (size_t)((T*)0 + 1))
//针对T为一个变量或者数组名的情况
#define array_sizeof(T)   ( (size_t)(&T+1)  - (size_t)(&T)  )

我们自己去使用这两个宏,确实可以替换掉sizeof,这是为什么呢?我们来分析一下上面的两个宏。

size_t这里不做解释,可以去查询其他资料,这里可以简单的理解为int类型

第一个宏:

转换为我们传入的类型就能+1就能直接得到结果了,这是为什么?

先给出结论,待会在进行解释,这就是因为指针步长的原因。指针步长导致了+1之后,指针的移动距离,就能得出该类型变量所占空间。

第二个宏:

int a[5];
printf("%d\n", a);
printf("%d\n", &a);

结果

1245036
1245036

由运行结果可知,数组名a和&a得内存地址相同。我们由数组和指针的关系知道,a代表这个数字,它相当于一个指针,指向第一个元素(&a[0]),即指向数组的首地址。数组中的其他元素可以通过a的位移得到,此时的步长是以数组中单个的元素类型为单位的。所以有a+1为1245040,即数组中a[1]的地址是1245040(在首地址1245036基础上加int的字节数4得到的)。

    &a取的是整个数组元素的地址。虽然&a和a得内存地址相同,但它们的意义不相同,它是代表整个数组的,它的步长单位是整个数组的字节长度(这里是4*5=20),所以&a+1得内存地址为1245056。

a的类型是int[5]   数组,一种特殊的变量类型
&a的类型是int(*)[5]  指针——指向int[5]数组的指针
&a[0]的类型是int*    指针——指向int类型的指针

我们都知道指针是有类型的,你对一个什么样的变量取地址,取出来的指针就是一个什么类型的。这一点其实很好理解,虽然说指针是一个地址,按照我们常规的实现来说,地址就是一个point,那为什么指针还需要类型?他的类型不应该是内存的大小吗?确实很容易陷入这样一个误区。我们换一个角度思考,我们存在&符号,同时也存在*符号。我们知道*符号能取出指针指向的值,所以指针必须要是有类型的啊。不然*符号怎么能知道要去取几个字节的数据。正是因为指针有不同的类型,所以不同的指针+1能,其地址的增长值才会不同,这就被称之为指针步长。

有以上可以得出,如果我们传入的是一个int* a指针类型,他虽然指向数组的首位置,但是他的指针类型是int,所以sizeof结果为4,这也就能得出为什么我们使用malloc动态分配的数组,不能使用sizeof去测量出数组长度了。而数组名这种指针就很特殊了,他的指针步长是由分配空间的长度所决定的,所以就能得出正确的结果。

 

还有一个问题,指针步长,程序是怎么知道的呢?

在运行时,所有的指针原则上一律平等。

而对指针进行+1操作时,到底加几的问题,是编译时决定的。

指针不需要记住自己的步长,编译器会将指针的类型映射到的汇编语言的数据类型,比如32位的机器,一个C语言里的int 型(4字节),对应到汇编语言里的就是双字(DD)define doubleword(一个word就是2个字节)。所以指针移动一个步长,到汇编层,也能知道具体是移多少。汇编层会去处理这个步长。结构体步长 或是 数组步长 的移动也是类似的道理。

所以说sizeof其实在编译时期,就能得出结果了,编译器还真是干了不少事。

 

这就又出现了一个新的问题,C99的变长数组

    int n;
    scanf("%d",&n);
    int a[n];
    printf("%d\n",sizeof(a));

居然也能得出正确的结果!!!!说好的编译期呢?

这个变长数组的特性必定把数组的分配引入了运行时期,附带的,sizeof这个原来的编译期操作也不得不被带入到运行时期。那么,对于动态数组的实现,编译器必定要生成一些代码来对其进行runtime的支持,至于怎么实现的,抱歉我不知道!!但是有一点肯定可以清楚,那就是性能上,肯定会有影响。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值