外观
08.混淆指针与数组的问题、volatile,让我保持原样
约 1099 字大约 4 分钟
嵌入式Linux单片机个人
2022-06-17
十六、混淆指针与数组的问题
1. 下面的程序输出什么?

2. 实例分析:指针 == 数组 ?
main.c
define.c

3. 再论指针和数组
指针
- 本质为变量,保存的目标值为内存地址
- 指针运算与*操作符配合使用能够模拟数组行为
数组
- 数组是一段连续的内存空间
- 数组名可看做指向数组第一个元素的常量指针
4. 在C语言层面中的等价关系

5. 问题:在二进制层面,指针和数组是否等价?
6. 编程实验:数组与指针深度分析
test.c

7. 结论
- 指针与数组在二进制层面的操作完全不同
- 指针操作:先寻址,再对地址单元进行操作
- 数组操作:直接针对地址单元进行操作
8. C/C++编译器的天生缺陷
- 由4个子部件组成(预处理器,编译器,汇编器,链接器)
- 每个子部件独立工作,相互之间没有通信
- 语法规范只在编译阶段(编译器)有效(如:类型约束,保护成员)
- 编译器部件对各个源文件进行独立编译(认为源文件相互独立)
9. 问题本质

10. 编程实验:本质分析


11. 解决方案
- 尽可能不使用跨文件的全局变量(非static全局变量)
- 当必须使用时,在统一固定的头文件中声明(global.h)
- 使用跨文件全局变量的源文件直接包含global.h
- 定义跨文件全局变量的源文件也需要包含global.h
12. 小贴士:声明和定义不同
- 声明只是告诉编译器,目标存在,可使用
- 定义实际为目标分配内存(变量)或确定执行流(函数)
- 理论上,任何目标都需要先声明,再使用
- C/C++语言允许声明与定义的统一
13. 编程实验:解决方案
global.h

14. 小结
- C/C++语言中的指针与数组在部分情况可等价
- 指针与数组在二进制层面的操作完全不同
- C/C++编译器忽略了源码间的依赖关系
- 在统一固定的头文件中声明跨文件使用的全局变量(global.h)
- 使用跨文件全局变量的源文件直接包含global.h
十七、volatile,让我保持原样
1. 嵌入式开发中的常见情形


2. 实例分析:设备启动及初始化
main.c
device.c
未优化

已优化

3. 问题分析
- 编译优化时,编译器根据当前文件进行优化
- 编译器能够发现变量 “不可能” 被改变(const全局变量)
- 为了效率上的提高,编译将变量值进行缓存
- 缓存的方式为:把变量值从内存中读取进入寄存器
- 每次访问变量时直接从寄存器读取对应值
4. 解决方案
- 使用volatile修饰可能被 “意外” 修改的变量(内存)
- volatile修饰的变量是一种 “易变的” 变量
- volatile可理解为 “编译器警告指示字”
- volatile告诉编译器必须每次去内存中取变量值
5. 编程实验:使用volatile修饰变量
main.c
device.c



6. 问题
如何理解const和volatile同时修饰变量?在语义上是否矛盾?
7. const 和volatile
- const表示被修饰的变量不能出现在赋值符号左边
- volatile表示使用变量时直接从内存取值
- const和volatile同时修饰变量时互不影响其含义

8. 小结
- 编译优化时,编译器只根据当前文件进行优化
- 编译器的优化策略可能造成一些“意外”
- volatile强制编译器必须每次从内存中取变量值
- const和volatile同时修饰变量时互不影响其含义
