int* sum(int x, int y) {
int a = x + y;
return &a; // 回傳區域變數位址 → 懸空指標
}
區域變數存於棧,函數返回後棧幀失效,位址內容隨時可能被覆寫。



C11 §6.2.4/2:區域變數生命週期結束於函數返回。存取已結束生命週期的物件 → 未定義行為。
表現可能為:正常輸出、亂碼、段錯誤、沉默資料損毀、跨平台行為不同。
| 方法 | 適用場景 | 範例 |
|---|---|---|
| 回傳值 | 小型資料 | int sum(int x, int y) { return x+y; } |
| 輸出參數 | 需修改呼叫端變數 | void sum(int x, int y, int *out) { *out = x+y; } |
| 動態配置 | 大型/可變大小資料 | int* sum(...) { int *p=malloc(sizeof(int)); *p=x+y; return p; }(呼叫端須 free) |
| static 區域 | 單例、非執行緒安全 | int* sum(...) { static int r; r=x+y; return &r; } |
| Thread-local | 多執行緒各自副本 | _Thread_local int tls; // C11 |
// GCC/Clang
-fstack-protector-strong -fsanitize=address,undefined
// MSVC
/GS /RTCs /RTCu
AddressSanitizer 會在存取懸空指標時立即報錯:
=================================================================
==12345==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffc12345678
READ of size 4 at 0x7ffc12345678 thread T0
#0 0x40123a in main /test.c:15
#1 0x7f... in __libc_start_main
SUMMARY: AddressSanitizer: stack-use-after-scope
=================================================================
文章中的多種變體(getsum、sum1、全域指標 x)皆證明:相同棧深度、相同區域變數大小 → 相同位址。但任何微小改動(加區域變數、改呼叫順序、開啟最佳化)即可打破巧合。