全局变量加/不加 volatile 的影响

💡 原文中文,约1200字,阅读约需3分钟。
📝

内容提要

全局变量问题由编译器优化导致。使用关键字“volatile”可以避免优化,确保每次直接读取其值。常见使用情况有访问外设和多线程共享变量。未声明为volatile的变量可能导致代码陷入循环或表现异常。

🔎

延伸解读

编译器优化如何引发全局变量问题

文章指出,全局变量问题常由编译器优化导致。编译器会假定未用volatile修饰的变量值不会在定义范围外被修改,从而可能删除或重排访问指令。这种优化在普通程序中通常无害,但在涉及硬件状态或并发访问时,会导致代码行为异常,例如轮询循环无法退出。

volatile的适用场景与必要性

文章列举了volatile的常见使用场景:访问内存映射外设、多线程共享全局变量、中断例程或信号处理程序中的全局变量。在STM32代码中,__IO即volatile的宏定义。对于跑系统的项目,线程间共享的全局变量建议加上volatile,但很多人并未注意这一点。

未使用volatile的潜在风险

未用volatile声明变量时,编译器可能执行不必要的优化,表现为:轮询硬件时陷入循环、多线程代码行为奇怪、故意时序延迟的代码被删除。文章举例说明,一个简单的延时函数在不同优化等级或不同编译器(如Keil和IAR)下,延时时间可能不同,因为循环可能被优化。

优化等级与编译环境的影响

文章强调,在不同优化等级和编译环境下,代码表现可能不同。同样的延时函数,在Keil和IAR下编译出的延时时间可能有差异。要深入理解这些差异,需要查看编译后的汇编代码,它能直观呈现优化带来的变化。

❓

Q&A

全局变量不加 volatile 会导致什么问题?

编译器会假定该变量的值不会在其定义范围之外被修改,从而可能执行不必要的优化,导致代码陷入循环、多线程行为异常或删除故意设计的延时。

volatile 关键字的作用是什么?

volatile 是一种类型修饰符,作为指令关键字,确保该指令不会被编译器优化省略,并要求每次直接读取变量的值。

哪些情况下需要使用 volatile 声明变量?

常见情况包括:访问内存映射外设、在多个线程之间共享全局变量、在中断例程或信号处理程序中访问全局变量。

为什么不同优化等级或编译环境下延时函数的延时时间会不同?

因为编译器可能对未用 volatile 声明的变量进行优化,删除或改变实现延时的代码,导致在不同优化等级(如 Keil 和 IAR)下编译出的延时时间不一致。

在 STM32 代码中,__IO 和 volatile 有什么关系?

在 STM32 代码中,__IO 就是 volatile 的别名,用于修饰可能被意外修改的变量,常见于访问内存映射外设。

多线程共享全局变量时,为什么建议加 volatile?

因为编译器可能假定未用 volatile 声明的变量值不会在定义范围外被修改,从而进行优化,导致多线程代码表现出奇怪的行为。加上 volatile 可以确保每次直接读取其值,避免优化带来的问题。

🏷️

标签

➡️

继续阅读