volatile。它常出现在寄存器、中断标志位、状态变量这些地方,但也特别容易被误解。
很多新手会觉得:volatile不就是修饰变量的吗?甚至怕出问题,所有变量都加一个。其实这玩意不是护身符,用错了照样坑。
先说结论:
volatile是用来告诉编译器:这个变量或地址可能被程序之外的因素改变,每次访问都必须真实发生,不要缓存,不要乱优化。
1. 一个典型坑
比如读取 GPIO 输入寄存器,等待引脚变成高电平:
unsignedintgpio_val = *(unsignedint*)0x12345678;while(gpio_val ==0) {// 期望一直读取 GPIO 状态}
这段代码看起来没毛病,但实际可能直接死循环。
原因很简单:gpio_val只在进入循环前读了一次。后面 while 判断的一直是这个普通变量,而不是重新读取硬件寄存器。
正确写法应该是:
while(GPIO_IN_REG ==0) {// 每次循环都会重新读取寄存器}
这就是volatile的价值:防止编译器把硬件寄存器当普通变量优化。
2. 为什么嵌入式必须懂 volatile?
在嵌入式里,很多变量或地址的值,不一定由当前代码修改。
比如:
如果不加volatile,编译器可能认为这个值“不变”,于是缓存到 CPU 寄存器里,后续不再真实访问内存或硬件地址。
这在普通 C 程序里可能没问题,但在嵌入式里就容易出现“调试看着正常,运行直接抽风”的问题。
3. 硬件寄存器必须加
典型写法如下:
unsignedintread_gpio(void){returnGPIO_IN_REG;}voidset_gpio_output(void){GPIO_CTRL_REG |= (1<< 0);}
读取寄存器时,必须每次读到硬件最新值;写控制寄存器时,也必须真实写入硬件,不能被编译器省略或延迟。
4. 几个常见误区
误区一:volatile 能保证原子性。
错。volatile只保证每次真实访问,不保证操作不可打断。
volatile int cnt;
cnt++;
cnt++本质是“读-改-写”三步,多线程同时执行仍然会有竞态。该用锁、原子操作时,还是要老老实实用。
误区二:所有变量都加 volatile 更安全。
错。volatile会限制编译器优化,滥用会降低效率。普通局部变量、临时计算变量,不需要加。
误区三:volatile 不能和 const 一起用。
也错。比如只读硬件寄存器:
#define ADC_DATA_REG (*(volatile const unsigned int *)0x12345680)
const表示程序不能写,volatile表示硬件可能改。两者并不冲突。
5. 总结
一句话记住:
volatile防的是编译器优化,不是防多线程竞态。
它常用于硬件寄存器、中断共享变量、DMA 状态标志、轮询等待外部状态等场景。
但它不能替代锁、原子操作、内存屏障和 cache 一致性维护。
所以,volatile的正确姿势不是“能加就加”,而是:该加的地方必须加,不该加的地方别乱加。
(完)
本人专注Linux 嵌入式全栈开发,可提供从硬件方案评估与设计、Linux/Android BSP 适配、驱动开发、外设调试、系统移植到产品交付的全流程技术支持。
-
嵌入式
+关注
关注
5212文章
20792浏览量
338987 -
volatile
+关注
关注
0文章
47浏览量
13823
发布评论请先 登录
嵌入式前景到底怎么样?
到底什么是嵌入式?我们该如何学习嵌入式
嵌入式软件编程中const、static、extern和volatile的原理是什么?怎么使用?
关于嵌入式的技术竞争力的相关资料分享
嵌入式软件开发过程
Volatile关键字对于嵌入式开发有什么作用呢
嵌入式软件测试的秘诀有哪些
嵌入式程序员常见的const、static、volatile关键字
通用嵌入式基础技术
到底什么是嵌入式?
【嵌入式】C语言中volatile关键字
Volatile关键字在嵌入式开发中的应用
嵌入式分享#62:volatile 到底在防谁?
评论