首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

C 語言棧記憶體佈局揭秘:為什麼 k[10] 會覆蓋變數 i

     編輯:程式設計綜合問答

現象重現

int m = 1;
int i = 0;
int k[10] = {0};

printf("&m   = %p\n", &m);
printf("&i   = %p\n", &i);
printf("&k[0] = %p\n", &k[0]);
printf("&k[9] = %p\n", &k[9]);
printf("&k[10] = %p\n", &k[10]);  // 越界訪問!

for (; i <= 10; ++i) {
    k[i] = 0;  // 當 i=10 時,寫入 k[10] 覆蓋了變數 i
}
printf("HelloWorld!\n");  // 永遠不會執行,陷入死循環

記憶體佈局圭示 (典型 x86 釋出構建)

高地址
+------------------+
|  main 參數/返回地址 |
+------------------+
|  儲存的 EBP      |  ← EBP
+------------------+
|  k[0] ~ k[9]     |  40 bytes (10 * 4)
|  k[10] (越界)    |  ← 這裡剛好覆蓋 i 的位置!
+------------------+
|  i (4 bytes)     |  ← 被 k[10] 覆蓋
+------------------+
|  m (4 bytes)     |
+------------------+
低地址 (棧增長方向 ↓)

為什麼會這樣排列?

  1. 區域變數在棧上分配,編譯器決定相對位移
  2. 聲明順序不等於記憶體順序,編譯器可自由重排以滿足對齊
  3. 對齊需求:int[10] 可能按 16 位元組對齊,導致後面有填充位元組
  4. 編譯器保護機制:部分編譯器 (MSVC / GCC -fstack-protector) 故意在陣列後留出「紅區」檢測越界

不同編譯器行為差異

標準規定:未定義行為 (UB)

C 標準 (C99 §6.5.6/8) 明確規定:**陣列越界訪問屬於未定義行為**。編譯器可以:

  • 產生看似「正常」但邏輯錯誤的程式碼 (如本例)
  • 直接崩潰
  • 優化時假設不會越界,導致邏輯錯誤
  • 任何其他行為

如何檢測與預防

// 1. 靜態分析
// GCC/Clang: -Wall -Warray-bounds
// MSVC: /Wall (較吵) 或 /analyze
// 專用工具: cppcheck, clang-tidy, Coverity

// 2. 執行期檢測
// GCC/Clang: -fsanitize=address -fno-omit-frame-pointer
// MSVC: /RTCs (Debug 構建)

// 3. 程式碼層面防禦
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))

for (i = 0; i < ARRAY_SIZE(k); ++i) {  // 嚴格 < 而非 <=
    k[i] = 0;
}

// 4. C++ 現代化替代
std::array k = {0};  // .at(10) 會拋出 out_of_range
// 或
std::vector k(10);

經典面試延伸題

「如果將 int k[10] 改為 char k[10],會發生什麼?」

答:char 1 位元組,k[10] 偏移 10 位元組。若 i 在 4 位元組對齊地址,可能不會被覆蓋,但仍屬 UB,行為不可靠。

總結

這個「經典面試題」實際上展示的是 **C 語言不做邊界檢查** 與 **棧幀佈局由編譯器決定** 的特性。**永遠不要依賴越界訪問的具體表現**,而應用工具與規範在編譯/執行期捕獲它們。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
編譯器/選項k[10] 位置結果
GCC -O0 (預設)緊接 k[9] 後高概率覆蓋 i → 死循環
GCC -O2可能優化掉整個循環行為不可預測
MSVC /RTCs (檢查)插入填充/金絲雀值運行時斷言失敗
Clang -fsanitize=address紅區保護即時報錯: stack-buffer-overflow