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=48printf("%d\n",sizeof(a[0][0]));/a的第一行第一个元素是0,类型为int,结果就为4printf("%d\n",sizeof(a[0]));/a[0]代表二维数组第一个元素,第一个元素是它的第一行整个数组的地址,
也就相当于一个一维数组的数组名,单独放在sizeof里为求它大小,为16printf("%d\n",sizeof(a[0]+1));a[0]作为第一行数组名,没有单独放在sizeof内部,a表示第一行地址
那么a+1就是第二行地址,是地址,所以大小为4printf("%d\n",sizeof(*(a[0]+1)));/a[0]+1是第二行数组首元素的地址,对它解引用就是0,类型为int
大小为4printf("%d\n",sizeof(a+1));/a数组名没有单独放在sizeof内部,没有&,所以a为第一行整体的地址,a+1
就是第二行整体的地址,类型为int(*)[4]的数组指针,32位环境下结果为4。printf("%d\n",sizeof(*(a+1)));/同上,对数组指针解引用,a+1是整个第二行数组的整体地址,对它进行
第一次解引用就是第二行首元素的地址,又因为数组名等于首元素地址,所以在这*(a+1)相当于一维数组的数组
名,所以为求整个第二行数组的大小,结果为16printf("%d\n",sizeof(&a[0]+1));/a[0]为第一行首元素的地址,对它取地址就相当于二维数组a首元素的
地址,二维数组首元素地址是第一行整体的地址,加一就是第二行整体的地址,所以&a[0]+1 -> a+1 ,是地
址,所以大小为4printf("%d\n",sizeof(*(&a[0]+1)));/同上,对a+1解引用就是第二行第一个元素的地址,相当于一维数组
的数组名单独放在sizeof里,求第二行数组的大小,为16printf("%d\n",sizeof(*a));/a是二维数组的数组名,也就是首元素的地址,,就是第一行第一个元素的地址
相当于一维数组名单独放在sizeof里,求第一行数组的大小,为16printf("%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。

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

  1. 二维数组解引用解释——指针

    #include <stdio.h>int main() {int array[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};int (*p)[4] ...

  2. C语言 指针+二维数组详解 (应付期末、考研的最强笔记,建议收藏)

    哈喽!这里是一只派大鑫,不是派大星.本着基础不牢,地动山摇的学习态度,从基础的C语言语法讲到算法再到更高级的语法及框架的学习.更好地让同样热爱编程(或是应付期末考试 狗头.jpg)的大家能够在学习阶段 ...

  3. 【C语言】二维数组定义以及引用

    接着上面一期进行,二维数组的一些了解,代码均来自于VS编译环境下. 一.怎样定义二维数组 二维数组和一维数组一样,二维数组的定义方式如下:   类型说明符 数组名[常量表达式][常量表达式] 注意一个 ...

  4. C语言基础入门48篇_30_二维数组的定义与使用(二维数组的定义:type 数组名[行][列]、二维数组的初始化、二维数组的引用)

    1. 二维数组的定义 type 数组名[行][列] 2. 二维数组的初始化 2.1 全部初始化为0 char chAry[2][3] = { 0 }; 实例: #include <stdio.h ...

  5. 指针学习中二维数组解引用问题

    在平常定义数组的时候常使用 int arr[5]={1,2,3,4,5} 在提取数组arr中的值的时候,会用arr[0],arr[1],arr[2]-来表示数组中的某个元素 学习中发现,方括号的意义其 ...

  6. 062是c语言常量吗,C语言ch062二维数组.pptx

    <C语言ch062二维数组.pptx>由会员分享,可在线阅读,更多相关<C语言ch062二维数组.pptx(33页珍藏版)>请在人人文库网上搜索. 1.2020/11/11,1 ...

  7. go语言定义二维数组

    使用go语言二维数组 go语言不用管理内存,很多地方使用起来确实很方便,但是在算法方面确实没有C++优秀,特别是缺少像STL一样优秀模板,定义一个二维数组需要进行如下复杂的操作: go语言的二维数组定 ...

  8. c语言调用二维数组作为函数参数传递,C++ 二维数组作为形参传递使用实例

    在线代码编辑器: http://codepad.org/ 1.*指针 void display(int *arr, const int row, const int col) { for(int i= ...

  9. java二维数组水平翻转,C 语言 利用二维数组实现对输入的数组进行翻转

    C 语言 利用二维数组实现对输入的数组进行翻转(帮助理解对图像翻转编辑原理) /* ?输入几行几列数字和翻转方式,如: 3 4 0即代表3行4列,左右翻转: 6 5 1即代表6行5列,上下翻转. 输入 ...

最新文章

  1. .Net并行库介绍——Task(1)
  2. java entry
  3. golang中的数字签名
  4. 工业用微型计算机(12)-指令系统(7)
  5. WaitForSingleObject
  6. 阿里云混合云Apsara Stack 2.0发布,加速政企数智创新
  7. 【集训队作业2018】复读机【指数型生成函数】【单位根反演】【二项式定理】
  8. C. Code a Trie(Trie+dfs+贪心)
  9. 2015/12/15--Document对象
  10. [vue] 说说你对provide和inject的理解
  11. sync - 清空文件系统缓冲区
  12. python带界面的人脸识别_PyQt5+Caffe+Opencv搭建人脸识别登录界面
  13. 使用可靠多播与OPENDDS进行数据分发
  14. java万年历系统的设计,基于安卓Android平台万年历系统设计与实现
  15. SpringBoot整合银联支付
  16. thinkpad t480s黑苹果蓝牙使用
  17. 电脑怎么打开隐藏文件夹?1分钟搞定!
  18. Android SELinux 的认知以及 init 的相关知识,Linux 环境利用这2个模块进行白名单测试 -- 架构分析
  19. C语言程序设计教程习题和答案-C语言期末复习必看资料
  20. Windows10超详细esmini的源码安装与测试运行——OpenScenario播放器

热门文章

  1. MYSQL字段属性之int() 和 tinyint()的区别
  2. 提升代码覆盖率的经验
  3. 关于Linux的那些事儿--系统状态检测命令
  4. 工欲善其事,必先利其器之—使用ImageMagick处理图片
  5. set_sql_trace_in_session PLS-00201
  6. MOSFET的误启动发生机制-3
  7. 【路径规划】基于matlab蚁群算法机器人大规模栅格地图最短路径规划【含Matlab源码 1860期】
  8. C++哈夫曼树+哈夫曼编码的实现(双完整版)
  9. 三维荧光学习记录--在Origin中绘制三维荧光光谱图(补充)及荧光区域积分(FRI)
  10. GPS接收机学习小记(一)