深入理解C——const与volatile
理解编译器级别的类型限定符,const与volatile的实质原理。
深入理解C——const与volatile
1 const
1.1 概要
const 即 constant 的简记形式,作为C语言的关键字,实现的是类型限定符的作用。
类型限定符可以与任何类型说明符一起使用。可以对const对象进行初始化,但在初始化之后不能进行赋值。……const用于声明可以存放在只读存储器中的对象,并可能提高优化的可能性。……除了诊断显式尝试修改const对象的情况外,编译器可能会忽略这些限定符。
(Kernighan&Ritchie 《The C Programming Language(2nd Edition)》)
const用于实现常量变量,通过编译器检查诊断的方式,避免对const变量的二次赋值。
1.2 使用
1 | /* 定义const变量a */ |
1.3 实质
const实现的不是真正意义上的常量!
const机制是编译器检查实现的,不是运行时保护,运行时无const约束。
const变量仅仅是编译时的“伪常量”。
具体说来:
1 | int const a=10; |
- 此例中,通过取const变量的地址并强制类型转换为(int *)非const的int型指针,此地址赋值给int指针变量p,通过指针p,即绕过了编译器的const常量约束检查,成功地在运行时实现对const的修改。
- 真正的只读常量,编译后存储在目标文件的
.ro.data
区。
1.4 目标文件的存储安排
之所以称const变量为“伪常量”,不仅是因为其根据的是编译器的编译时诊断检查来实现常量效果,而且const变量的存储区域和普通变量别无二致。全局变量存储在.data
段或记录在.bss
段,局部变量则在程序的临时栈中管理。
编译过后生成的目标文件中,包含以下几个与变量相关的存储段:
.text
- 代码段
- 存储代码以及立即数等数据,只读常量。
.ro.data
- 只读(readonly)段 - 数据区
- 真正的常量,如:字符串常量,存储在目标文件的
.ro.data
区,此区域是真正的只读常量。
.data
- 数据段
- 存储已初始化为且值为非0的全局变量。
.bss
- BSS段(Block Started by Symbol)
- 记录未初始化的全局变量。此段的变量将被默认初始化为0。此部分仅仅是占位符,数据不占用实际的磁盘空间。
2 volatile
volatile声明易变常量,即告知编译器该变量可能被代码以外的因素改变,如:中断服务例程(ISR),多线程中的异线程修改,或硬件修改等。
volatile用于强制某个实现屏蔽可能的优化。例如,对于具有内存映像输入/输出的机器,指向设备寄存器的指针可以声明为指向volatile的指针,目的是防止编译器通过指针删除明显多于的引用。
(Kernighan&Ritchie 《The C Programming Language(2nd Edition)》)
- 编译器遇到volatile变量,将不会对其做编译优化,以避免出现问题。