int main() {
const int a = 1;
int *b = (int*)&a;
*b = 21;
printf("%d, %d\n", a, *b); // 輸出: 1, 21 (!!)
return 0;
}
預期 a 被修改為 21,實際輸出 1, 21。地址相同,值卻不同。
// GCC -O2 x86-64 AT&T 語法
main:
subq $8, %rsp
movl $1, -4(%rsp) # a = 1 存入堆疊
movl $21, -4(%rsp) # *b = 21 寫入同一地址
movl $1, %esi # printf 參數 a: 直接用常數 1!
leaq -4(%rsp), %rdi # printf 參數 *b: 取地址值 (21)
call printf
xorl %eax, %eax
addq $8, %rsp
ret
關鍵點:movl $1, %esi — 編譯器將 a 視為編譯期常數,直接內嵌到 printf 參數,完全忽略記憶體實際值。
const int a = 1:初始值為編譯期常數。const 物件**不會被修改**(嚴格別名規則 + const 語義)。a 的地方替換為常數 1。a 屬於未定義行為(違反 const 契約),編譯器不保證行為一致。int main() {
int c = 1; // 非常數初始化
const int a = c; // 執行期賦值
int *b = (int*)&a;
*b = 21;
printf("%d, %d\n", a, *b); // 輸出: 21, 21 ✓
}
對應組合語言會正確從堆疊讀取 a 的值(movl -4(%rsp), %esi)。
const 修飾的物件 → **未定義行為**。const 僅承諾程式碼不直接修改;不保證物件在記憶體中不可變(可能在 ROM、受保護頁面)。const 物件不變,進行激進優化(常數傳播、死碼消除、環狀緩衝區優化等)。const 或使用 mutable (C++ 類成員)。volatile 而非 const。volatile uint32_t *REG = (volatile uint32_t*)0x40000000;volatile 或原子操作(std::atomic、GCC builtins)。const + __attribute__((section(".rodata"))) 確保進入 Flash,不可修改。gcc -S -O2 -fverbose-asm 確認優化行為。| 程式碼 | 優化行為 | 對策 |
|---|---|---|
int x = 0; while(x==0); | 假設 x 不變,優化為無限迴路 | volatile int x |
memcpy(dst, src, n); // n=0 | 刪除呼叫 | 正常,符合標準 |
float x = 1.0/3.0; if(x*3==1.0) | 編譯期計算可能不同於執行期 | 避免浮點精確比較 |
int a[10]; a[10]=0; | 越界存取,編譯器可能刪除或重排 | 啟用 -fstack-protector、靜態分析 |
const 是給**編譯器**和**讀程式碼的人**的承諾,不是執行期鎖。違反承諾(透過指標修改)會觸發未定義行為,編譯器優化會讓結果「不可思議」。嵌入式開發中,正確使用 const/volatile/restrict,比依賴編譯器「智能」可靠得多。