zoukankan      html  css  js  c++  java
  • 【C/C++】一道试题,深入理解数组和指针

    在x86平台下分析下面的代码输出结果

     1 int main(void)
     2 
     3 {
     4 
     5     int a[4] = {1, 2, 3, 4};
     6 
     7     int *ptr1=(int *)(&a+1);
     8 
     9     int *ptr2=(int *)((int)a+1);
    10 
    11     printf("%x, %x/n", ptr1[-1], *ptr2);
    12 
    13     return 0;
    14 
    15 }

    &a+1

      首先明确,a是一个具有4个整型变量的数组的名字,具体地说是这种数组的首元素的首地址,而&a是数组的首地址,请注意措辞。而关于指针加1,则需要指针运算的知识。

    下面就是关于指针运算你需要知道的事实:

    就像上面的例子那样,式子&a+1表示的是指针加法运算,而不是普通的数值加法运算,之所以会这样是因为&a是一个指针而非普通数值(虽然它本质上也是一个整数)。那么:假如此时&a=0xFFFF5700,那么&a+1是多少呢?答案是:取决于&a的类型 。

    • 如果&a是一个指向char型的指针,那么&a+1 = 0xFFFF5701
    • 如果&a是一个指向short型的指针,那么&a+1 = 0xFFFF5702
    • 如果&a是一个指向int型的指针,那么&a+1 = 0xFFFF5704 (32位机器)
    • 如果&a是一个指向某种结构体struct foo的指针,那么&a+1 = 0xFFFF5700+sizeof(struct foo)

    ……

      指针加1不是指针内容简单地加1,而是让指针指向下一个数据 ,加2就是让指针指向下两个数据,这个数据的类型就是指针指向的类型,所以指针的加法究竟会让这个指针指向哪里,取决于这个指针指向的数据类型。

      因此,综上所述,当&a与整数1做加法时,实际上是指针的加法,加1的含义是:令指针a指向下一个数据 ,下一个数据是啥?当然是紧挨着的下一个具有4个整型变量的数组了(因为&a的类型是指向具有4个整型变量的数组的指针嘛),于是a的指向了4的下一个地址,在用此值初始化ptr1,因此ptr1的指向如图所示:

      由于在ptr1初始化的时候,令&a+1强制转换成整型指针,因此ptr1[-1]相当于把ptr1往前挪一个整型大小,即4个字节。 如下图:

      显然,打印出的第一个数字是a[3]的内容,即数值4.

      还必须说明一个事实:数组下标是可以为负数的,实际上,取下标符“[ ]”的内部实现,就是指针运算!比如a[2],等价于*(a+2),即以a地址为基址,取偏移量为2的地址的值。所以ptr1[-1]等价于*(ptr-1)。


    (int*)((int)a+1)

      先把数组名a强制转换成整型变量,然后再加1,然后再强制转换成整型指针!真罗嗦,但不要紧,咱有的是耐性,无非就是让ptr2指向a[0]的第二个字节罢了,此时ptr2指向如下图所示:

      此时打印的内容就是ptr2所指向的往后4个字节的内容了,也就是a{0}的后三个字节和a[1]的第一个字节,那究竟会打印出啥玩意儿呢?

      要把上图中每一个字节的内容都打印出来没问题,但是先要知道字节序的概念,字节序分两种,一种叫大端字节序(big-endian) ,当然除此之外必然有小端 字节序(little-endian)

    官方解释:

    •     所谓大端(big-endian)序,就是高优先位 对应高有效位 
    •     所谓小端(little-endian)序,就是高优先位 对应低有效位 

    民间解释:

    •     所谓大端(big-endian)序,就是读取或者存放数据时,最低 位 对应 高地址 
    •     所谓小端(big-endian)序,就是读取或者存放数据时,最低 位 对应 低地址 

      回到原来的问题,此时ptr2指向了a[0]的第二个字节。以x86平台为例(小端序),此时其内部数据分布是这样的:

      由于x86平台是小端序的,小端序的存取时最低位 对应 低地址 ,因此将会打印出0200 0000,如果没有说明在x86平台,那答案是不确定的,取决于具体的平台,例如ARM平台就是大端序的。


    关于a 和&a

      考虑这个定义:

    int a[4] = {1, 2, 3, 4};

      这时我们必须明确,编译器根据我们提供的类型和数组大小,为我们分配了适当大小的存储区域,并且把这块存储区域叫做a.

      请注意 

    1. &a,就像我们上面提到的,它的类型是“指向具有4个整型变量数组的指针“,简而言之&a是一个数组指针。
    2. 这个数组的名字a,当它作为右值时代表的是数组首元素的首地址,而不是数组的首地址。此时a的意义跟&a[0]是等价的,都是指向首元素的指针。
    3. 数组的名字会在另一个地方与指针等价,那就是函数参数表,当出现在函数的参数表里面的时候,不管写的是数组,还是指针,一旦进入函数内部,通通变成指针。
  • 相关阅读:
    Windows下升级MySQL5.0到5.5
    聊聊MVC和模块化以及MVVM和组件化
    还有很多行业,并没有和互联网相加
    用React实现一个自动生成文章目录的组件
    一个Js开发者学习Python的第一天
    React弹窗组件
    React项目开发经验汇总
    Audio 标签的使用和自己封装一个强大的React音乐播放器
    你知道的和不知道的sass
    我眼中javascript的这些年
  • 原文地址:https://www.cnblogs.com/lcw/p/3188269.html
Copyright © 2011-2022 走看看