| 場景 | 範例 | 無 volatile 後果 |
| 記憶體映射硬體暫存器 | volatile uint32_t* UART_DR = (uint32_t*)0x4000C000; | 連續寫入被合併、讀取被快取,硬體協議失效 |
| 中斷服務程式 (ISR) 共享旗標 | volatile bool flag = false; // ISR 置 true,主迴圈檢查 | 主迴圈快取旗標值,永遠不見更新,死循環 |
| 多執行緒共享變數 (無鎖/原子操作前) | volatile int counter = 0; | 編譯器重排序/快取導致可見性失效(注意:volatile 不保證原子性,現代用 std::atomic) |
程式碼範例:ISR 旗標
volatile int interrupt_flag = 0;
void ISR_Timer() { // 中斷服務程式
interrupt_flag = 1;
}
int main() {
while (!interrupt_flag) { // 若無 volatile,編譯器可能優化為 while(true)
// 做其他事
}
handle_interrupt();
return 0;
}
volatile 與 const 可共存
const volatile uint32_t* STATUS_REG = (uint32_t*)0x40000000;
// 唯讀(程式不修改)+ 易變(硬體隨時更新)
volatile 指標
volatile int* p; // 指向 volatile int 的指標
int* volatile p; // volatile 指標(指標本身易變),指向 int
volatile int* volatile p; // 兩者皆 volatile
經典錯誤:volatile 不保證原子性
volatile int x = 0;
// 線程 A
x = x + 1; // 非原子:讀-改-寫,多執行緒下競態
// 正確:用原子操作
#include <stdatomic.h>
atomic_int x = 0;
atomic_fetch_add(&x, 1); // 原子增 1
volatile 陷阱:平方函數
// 錯誤
int square(volatile int* p) { return *p * *p; }
// 編譯器可能產生:讀取 *p 兩次,若中間被修改,結果非平方
// 正確
int square(volatile int* p) { int v = *p; return v * v; }
現代 C/C++ 替代方案
- 硬體暫存器:仍需
volatile(配合 __attribute__((io)) 等編譯器擴展)。
- 多執行緒同步:
std::atomic (C++11) / _Atomic (C11)、**鎖**/**條件變數**,volatile **不適用**。
- 編譯器屏障:
asm volatile("" ::: "memory") (GCC/Clang) 阻止重排序,不影響硬體可見性。
總結
volatile = **「直接記憶體存取,禁止編譯器優化」**。
- 三大場景:硬體暫存器、ISR 旗標、舊式多執行緒旗標。
- **不保證原子性、不保證記憶體順序、不替代鎖/原子操作**。
- 現代多執行緒代碼**禁用 volatile 做同步**,改用
std::atomic/mutex。
- 上一篇文章: C 語言開平方根:sqrt 函數用法
- 下一篇文章: C 語言 scanf 格式輸入函數完整指南
Copyright ©
程式碼之家 All Rights Reserved