首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言函數回傳區域變數位址:懸空指標、棧幀重用與未定義行為深度實測

     編輯:關於C語言

核心問題

int* sum(int x, int y) {
    int a = x + y;
    return &a;  // 回傳區域變數位址 → 懸空指標
}

區域變數存於棧,函數返回後棧幀失效,位址內容隨時可能被覆寫。

實測現象解析(VC2008 Release 模式)

第一次測試畫面

第二次測試畫面

free 嘗試畫面

為何「看起來正常」?

  1. 棧幀重用:同樣呼叫慣例、同樣參數、同樣區域變數佈局 → 相同位址被重複使用
  2. 資料未被覆寫:緊接著呼叫其他函數前,記憶體內容殘留
  3. 最佳化移除:Release 模式可能內聯、省略棧幀,行為更不可預測

標準定義:未定義行為

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)皆證明:相同棧深度、相同區域變數大小 → 相同位址。但任何微小改動(加區域變數、改呼叫順序、開啟最佳化)即可打破巧合。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved