volatile 變數:
• 禁止編譯器快取其值於暫存器。
• 每次讀寫必須產生實際記憶體存取指令。
• 禁止重排序優化 (相對於其他 volatile 存取)。
| 場景 | 範例 | 無 volatile 後果 |
|---|---|---|
| 記憶體映射硬體暫存器 | 狀態暫存器、資料埠、控制暫存器 | 編譯器合併重複讀寫,硬體操作失效 |
| 中斷服務程式 (ISR) 共享變數 | 旗標、緩衝區索引、計數器 | 主迴圈讀到舊暫存器值,錯過中斷事件 |
| 多執行緒共享旗標 (無鎖) | volatile bool stop_flag; | 執行緒快取舊值,無法及時退出 |
是。
範例:const volatile uint32_t *STATUS_REG;
• volatile:硬體可隨時更新。
• const:程式碼不可修改 (唯讀暫存器)。
是。
volatile int *ptr; → 指標值本身可能改變 (如 ISR 修改緩衝區指標)。
區別:
• volatile int *p:指向的 int 易變。
• int * volatile p:指標 p 易變。
• volatile int * volatile p:兩者皆易變。
int square(volatile int *ptr) {
return *ptr * *ptr; // 錯誤!
}
錯誤:編譯器會產生兩次讀取,若兩次間值改變,回傳非平方值。
正確:
long square(volatile int *ptr) {
int val = *ptr; // 單次讀取
return (long)val * val;
}
_Atomic/鎖/硬體指令)。// 硬體暫存器定義
#define REG_BASE 0x40000000
volatile uint32_t *const UART_DR = (volatile uint32_t*)(REG_BASE + 0x00);
volatile uint32_t *const UART_FR = (volatile uint32_t*)(REG_BASE + 0x18);
// 發送字元
void uart_putc(char c) {
while (*UART_FR & (1<<5)); // 等待發送 FIFO 不滿
*UART_DR = c;
}
// ISR 共享旗標
volatile sig_atomic_t gpio_irq_flag = 0;
void GPIO_IRQHandler() { gpio_irq_flag = 1; }
int main() {
while (!gpio_irq_flag) { /* 低功耗待機 */ }
// 處理中斷事件
}
volatile 是嵌入式 C 與系統程式設計的基石關鍵字,專門對抗編譯器「過度聰明」的優化。正確理解其語義邊界 (不保證原子性/可見性)、掌握 const volatile、volatile 指標 組合用法、避免 *ptr * *ptr 陷阱,是合格嵌入式工程師的核心技能。