当前位置: 首页 > news >正文

带你学C带你飞

失踪人口回归!!!……其实自己都不太好意思讲了,失踪了这么久。因为最近真的特别忙。忙到每天结束的时候都不会想再看电子屏幕,包括手机!(我知道你们的OS:闭嘴,都是借口!),好啦,我这不一有时间就过来码文啦~

记得上次我们讲了指针和指针数组,讲到指针数组实际上就是装满了指针的数组。今天我们讲一个和它听起来很类似的概念,数组指针。

1
指针的步长

要理解数组指针,首先要加深对指针的理解。指针的类型决定了指针的视野,指针的视野决定了指针的步长。我们必须清楚一个指针变量将告诉我们两个信息:某个数据结构的起始地址,以及该结构的跨度。比如 int p = &a; 说明该指针指向变量 a 的起始地址,以及它的跨度为 sizeof(int),所以 p + 1 == p + sizeof(int)。

因此不同的数组指针,步长不同。首先从数组名的角度进行考虑,对于一个一维数组 int array[3]={1,2,3}; ,数组名是数组的首地址,是一个 int 型的指针,这点很明显。对于一个二维数组 int array[2][3]={{1,2,3},{4,5,6}}; 来讲,由于内存中的二维数组是以一维数组的形式存放的,所以二维数组是嵌套定义的 ,这个二维数组就是由两个一维数组array[0]和array[1]组成的。其中

array[0]:是第一个一维数组的数组名,这个一维数组是{1,2,3};

array[1]:是第二个一维数组的数组名,这个一维数组是{4,5,6};

这个时候数组从 array[0] 到 array[1] 虽然只变换了一个位置,但实际上跳过了整个第一行,因此

array数组的元素类型为: int (*)[3]类型 //步长为 3 的指针

array[0]数组的元素类型为: int *类型

array[1]数组的元素类型为: int *类型

同理对于一个三维数组

640?wx_fmt=png

Sarray: 是指向 int (*)[2][3]类型的指针;//步长为 2行3列的指针。

Sarray[0]:是指向int *[3]类型的指针;

Sarray[0][0]:是指向int *类型的指针;

因此可以看到,对于数组名来讲,他的确是数组第一个元素的地址,但是他的步长是根据他的第一个元素的数量确定的,无论是多少维的数组,把它看成一个一维数组,里面包含着怎样的子数组,就有怎样的步长。

2
数组指针

数组指针,顾名思义,是一个指向数组的指针,比如说下面这个

640?wx_fmt=png

从运算符的优先级和结合性进行分析,因为圆括号和数组下标位于同一个优先级队列,所以我们就要看先来后到了。由于它们的结合性都是从左到右,所以 p2 先被定义为一个指针变量。那么它指向谁?还能有谁?后边还紧跟着一个具有 5 个元素的数组,p2 指向的就是它。由于指针变量的类型事实上就是它所指向的元素的类型,所以这个 int 就是定义数组元素的类型为整型。即如下图所示

640?wx_fmt=png

所以数组指针是一个指针,它指向的是一个数组。

那么数组名,数组第一个元素的地址,数组指针这三者之间是什么关系呢?

我们看下面的这段程序,它将打印出这3个指针的值,并且通过指针的方式打印出指针所指向的下一个元素的地址。

640?wx_fmt=png

输出的结果如下

640?wx_fmt=png

可以看到数组名,数组第一个元素的地址和数组指针这三个指针指向的都是同一个地址,那么是否意味着三者是完全等价的呢?在上面的代码中可以看到 pp 指针指向数组的首地址,他的下一个指向的数组中第二个元素的地址;而作为数组指针的 p 指针虽然也指向数组的首地址,但是他的下一个,却指向数组外面的位置(与数组首地址相差了 20,16 进制的 14 是 10 进制的20),只有通过 *p+1 才会得到数组中第二个原始的地址,这就涉及到指针步长这个概念了。

就像刚刚所讲的,实际上这里 &temp 对数组取址就是将整个数组看作是一个元素,那么指针 int (*p)[5] = &temp; 的跨度就很明显是 5 啦~这也就解释了为什么指向数组首地址的指针的下一个是数组中的第二个元素的地址,而数组指针的下一个会指向数组的最后(实际上是数组最后一个元素的后一个位置的地址)。

根据上面的知识可以知道,下面的代码明显是错误的。

640?wx_fmt=png

它的本来用意是想用指针法的形式将数组中的每一个元素打印出来,但是却得到如下的结果

640?wx_fmt=png

因此我们对代码进行了如下的修改,将数组指针初始化为整个数组的地址(即代码中的 5 6 行)。

640?wx_fmt=png

执行之后发现程序依然会报错,如下所示

640?wx_fmt=png

很明显又错了,从 warning 的信息来看 printf 使用一个 %d 作为占位符,但是传入的确实一个整型指针。这个很好理解,因为 p 指针还是一个指向数组首地址,步长为5的指针。

将程序修改为如下的形式便可以正常执行了

640?wx_fmt=png

结果就是输出 1 2 3 4 5 ,这里就不截图了。需要强调的是在打印输出的过程中使用了 printf("%d\n", *(*p2 + i)); ,在 p2 的前面增加了一个 * ,有两种方式理解这种改动。一种是 temp 前面多了一个取址运算符,p2 前面就要对应着增加一个取值运算符;另一种理解方式是,temp 是数组名,实际上就是数组中第一个元素的地址,对地址再进行取址,所以 p2 代表的是数组自一个元素地址的地址,这个时候 *p2 代表数组第一个元素的地址,*p2 + i 就是后面第 i 个元素的地址,然后再取值就可以获得数组中的元素了。

我们可以从下面这个例子加深自己对数组指针的理解

640?wx_fmt=png

如果将数组指针与数组第一个元素的地址等价起来的话,上面的代码很明显是越界了的,但实际上编译执行之后输出了结果 4。实际上是因为虽然 array 和 &array 的值相同,但步长是不一样的。

因此,&array + 1 指向的就是整个数组最后的位置(第二个 array 数组的起始位置),然后 (int *) 将其强制转换为一个整型地址(指针),所以指针变量 p 初始化后,指向的地址应该是 array[10](第 11 个元素),所以 *(p - 6) == array[10 - 6] == array[4] == 4。

重点强调:array 是数组第一个元素的地址,所以 array + 1 指向数组第二个元素;&array 是整个数组的地址,所以 &array + 1 指向整个数组最后的位置,也就是两者指针的步长是不一样的。

好了,今天的内容就到这里了,有没有比较烧脑呢?如果觉得我讲明白了请继续支持我们,如果觉得有疑问请积极留言提问~

3
参考

[1]  “小甲鱼” 视频课程《带你学C带你飞》【第一季】P23 

——//——

让知识成为信仰  让优秀成为习惯

往期精彩回顾
真的全都是干货
腾讯又在搞事情了
从拖延症晚期到自律,我是怎么做到的

640点击 阅读原文,关注我的CSDN

相关文章:

  • 程序员最全进阶资源免费送
  • 公众号征稿,50-150元/篇
  • 程序员提高核心竞争力的几点干货
  • 送10本书,清华大学出版社出版
  • 是谁杀死了锤子和ofo?
  • 互联网不再吃香?
  • 二维数组与语法糖
  • 几个很重要的建议
  • 苹果与高通大战,iPhone XS要成绝版?
  • 非985/211院校毕业的程序员怎么了?
  • 今天,我23岁了
  • 网易云音乐为什么这么懂你?
  • 你想去阿里吗?
  • Word 的一些神操作,你都会了吗?
  • 小米为什么要“抛弃”红米?
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • 10个确保微服务与容器安全的最佳实践
  • android图片蒙层
  • JDK 6和JDK 7中的substring()方法
  • jquery cookie
  • JS基础之数据类型、对象、原型、原型链、继承
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • js数组之filter
  • Just for fun——迅速写完快速排序
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • Linux各目录及每个目录的详细介绍
  • Linux后台研发超实用命令总结
  • Mithril.js 入门介绍
  • nfs客户端进程变D,延伸linux的lock
  • pdf文件如何在线转换为jpg图片
  • QQ浏览器x5内核的兼容性问题
  • Spring声明式事务管理之一:五大属性分析
  • Sublime text 3 3103 注册码
  • Vue 2.3、2.4 知识点小结
  • vue脚手架vue-cli
  • Vue--数据传输
  • webgl (原生)基础入门指南【一】
  • 得到一个数组中任意X个元素的所有组合 即C(n,m)
  • 多线程 start 和 run 方法到底有什么区别?
  • 服务器从安装到部署全过程(二)
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 来,膜拜下android roadmap,强大的执行力
  • 前端技术周刊 2019-02-11 Serverless
  • 设计模式 开闭原则
  • 无服务器化是企业 IT 架构的未来吗?
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • #1015 : KMP算法
  • #考研#计算机文化知识1(局域网及网络互联)
  • (11)工业界推荐系统-小红书推荐场景及内部实践【粗排三塔模型】
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (附源码)ssm码农论坛 毕业设计 231126
  • (转)ObjectiveC 深浅拷贝学习
  • (转)Sublime Text3配置Lua运行环境
  • (转)VC++中ondraw在什么时候调用的