volatile 告訴編譯器:「這個變量的值可能在程序不可控的情況下發生改變,請不要優化對它的訪問,每次都從內存讀取/寫入。」
適用場景:
環境:VS2012 Release 模式,優化選項 /O2 (最大速度優化)。
int i = 10;
int a = i;
tmp(a); // 輸出 10
__asm { mov dword ptr [ebp-4], 0x50 } // 內聯彙編修改 i 為 80
int b = i;
tmp(b); // 輸出 10 !編譯器優化掉了二次讀取
彙編對比 (關鍵片段):
; 無 volatile - 直接使用立即數 0Ah (10)
int a = i; mov eax, 0Ah
tmp(a); push 0Ah
; 內聯彙編修改 [ebp-4] 為 50h (80)
mov dword ptr [ebp-4], 50h
; 再次讀取 i - 仍使用 0Ah,完全忽略了內存修改!
int b = i; push 0Ah
volatile int i = 10;
int a = i; mov eax, dword ptr [i] ; 從內存讀取
tmp(a);
__asm { mov dword ptr [i], 0x50 } ; 修改內存
int b = i; mov eax, dword ptr [i] ; 再次從內存讀取,得到 80
tmp(b);
彙編對比:
; 有 volatile - 每次都從 [i] 內存地址讀取
volatile int i=10; mov dword ptr [i], 0Ah
int a=i; mov eax, dword ptr [i]
tmp(a);
__asm { mov dword ptr [i], 50h } ; 修改內存
int b=i; mov eax, dword ptr [i] ; 讀取更新後的值 80
tmp(b);
| 場景 | 無 volatile | 有 volatile |
|---|---|---|
| 讀取 | 可能使用寄存器緩存值 | 強制從內存讀取 |
| 寫入 | 可能延遲寫回內存 | 強制立即寫入內存 |
| 指令重排 | 編譯器可自由重排 | 相對於其他 volatile 訪問不重排 |
| 死代碼消除 | 可能刪除「無效」讀寫 | 保留所有訪問 |
volatile int* p1; // 指向 volatile int 的指針 (常用)
int* volatile p2; // volatile 指針 (指針本身易變)
volatile int* volatile p3; // 兩者皆 volatile
const volatile int status_reg; // 只讀硬件狀態寄存器
// 程序不修改 (const),但硬件可改 (volatile)
// 錯誤示範:volatile 不能替代 mutex/atomic
volatile int counter = 0;
void thread_func() { for(int i=0;i<10000;++i) counter++; } // 數據競爭!
// 正確:使用 std::atomic
std::atomic counter = 0;
// 錯誤:返回 *ptr * *ptr 可能非平方值
int square(volatile int* ptr) { return *ptr * *ptr; }
// 正確:先讀取到局部變量
long square(volatile int* ptr) { int val = *ptr; return val * val; }
volatile struct Regs* HW = (Regs*)0x40000000;。MemoryBarrier() 或原子操作實現無鎖編程時,volatile 是基礎但不夠。volatile 是告訴編譯器「別動這個變量的優化」的關鍵字。它確保內存訪問的可見性,但不提供原子性、不保證線程安全、不建立 happens-before 關係。現代 C++ (C++11 及以上) 多線程編程中,請優先使用 std::atomic、std::mutex、std::thread 等標準庫設施,將 volatile 保留給其真正的應用領域:硬件交互與特殊優化抑制。