volatile 是型別修飾詞,告訴編譯器:該變數可能被「編譯器未知因素」 (作業系統、硬體、其他執行緒、訊號處理常式) 修改,禁止優化相關存取,確保每次直接從記憶體讀寫。
volatile int flag; // volatile 修飾 int
int volatile flag; // 等價寫法
volatile int* ptr; // 指向 volatile int 的指標
int* volatile ptr; // volatile 指標 (指標本身不可優化)
volatile int* volatile ptr; // 兩者皆 volatile
| 場景 | 說明 | |
|---|---|---|
| 記憶體對應暫存器 | 硬體暫存器映射到記憶體位址,讀寫有副作用 | |
| 中斷服務常式 (ISR) 共用變數 | ISR 修改旗標,主迴輪詢檢查 | |
| 多執行緒共用旗標 (無鎖) | 簡單布林旗標,配合記憶體柵欄使用 | |
| signal handler 修改變數 | 非同步訊號處理常式修改全域變數 | |
| 嵌入式系統記憶體映射 I/O | GPIO、UART、定時器等外設暫存器 |
| 建置模式 | 無 volatile | 加 volatile |
|---|---|---|
| Debug (無優化) | 10, 32 ✓ | 10, 32 ✓ |
| Release (優化) | 10, 10 ✗ (快取值) | 10, 32 ✓ (重新讀取) |
結論:Release 模式下編譯器優化導致第二次讀取使用暫存器快取值,volatile 強制重新從記憶體載入,修正錯誤行為。
; 無 volatile
mov eax, dword ptr [ebp-4] ; 第一次讀取
mov dword ptr [ebp-8], eax ; 存入 a
; ... asm 修改記憶體 ...
mov eax, dword ptr [ebp-8] ; 直接用 a 的值 (快取) — 錯誤!
; 有 volatile
mov eax, dword ptr [ebp-4] ; 第一次讀取
mov dword ptr [ebp-8], eax
; ... asm 修改記憶體 ...
mov eax, dword ptr [ebp-4] ; 重新從記憶體讀取 — 正確!
volatile ≠ 執行緒同步原語。不保證原子性、不建立記憶體柵欄、不防止指令重排 (多核心)std::atomic (C++11)、std::mutex、InterlockedXXX (Windows)volatile 僅用於「編譯器看不見的修改源」:硬體暫存器、ISR、signal handler、setjmp/longjmp 變數std::atomic<T> 提供 volatile 語意 + 原子性 + 記憶體序控制,全面取代多執行緒場景下的 volatilevolatile 是告訴編譯器「別聰明,老實讀寫記憶體」的指令。VC6 實測確認其在優化模式下抑制快取行為。現代多執行緒程式設計請改用 std::atomic,將 volatile 保留給硬體介面、訊號處理等真正需要的場景。