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

C語言函數調用棧幀演變全過程圖解

     編輯:關於C語言

x86 架構下函數調用的棧幀機制

理解函數調用過程中棧幀的建立與銷毀,是掌握 C 語言運行時機制、調試崩潰堆棧、編寫高性能代碼的基礎。

棧的生長方向與關鍵寄存器

  • 棧向下生長:從高地址向低地址擴展。
  • EBP(基址指針/幀指針):固定指向當前棧幀底部(高地址),作為局部變量、參數訪問的基準錨點。
  • ESP(棧指針):動態指向棧頂(低地址),隨入棧/出棧自動移動。

函數 A 調用函數 B 的完整棧幀變遷

  1. 調用前:A 的棧幀完整,EBP 指向 A 棧底,ESP 指向 A 棧頂。
  2. 參數入棧:按調用約定(如 cdecl 從右向左)將參數壓棧。
  3. 返回地址入棧:CALL 指令將下一條指令地址壓棧。
  4. 保存舊 EBP:PUSH EBP,將調用者 A 的 EBP 保存。
  5. 建立新幀基址:MOV EBP, ESP,以當前 ESP 為 B 的棧底。
  6. 分配局部變量空間:SUB ESP, N,在棧上預留空間。
  7. 執行函數體:通過 EBP + offset 訪問參數,EBP - offset 訪問局部變量。
  8. 返回序列:
    • MOV ESP, EBP(或 LEAVE):恢復 ESP 到幀基址,釋放局部變量空間。
    • POP EBP:恢復調用者 EBP。
    • RET:彈出返回地址跳轉,調用者再清理參數(cdecl)。

AT&T 汇編對應指令

leave    # 等價於 mov %ebp, %esp ; pop %ebp
ret      # 彈出返回地址並跳轉

圖示化棧佈局(高地址在上)

[高地址]
|  調用者 A 的局部變量   |  <- EBP_A (舊 EBP)
|  返回地址 (指向 A 後續) |  <- ESP 執行 CALL 後
|  被調用者 B 的舊 EBP   |  <- EBP_B (新幀基址)  <- PUSH EBP 後
|  B 的局部變量空間      |  <- ESP (棧頂)         <- SUB ESP 後
[低地址]

實戰意義

  • 調試:GDB bt 回溯堆棧正是沿 EBP 鏈遍歷。
  • 緩衝區溢出:局部數組寫越界會覆蓋舊 EBP、返回地址,導致控制流劫持。
  • 尾調用優化:編譯器若檢測到尾調用,可重用當前棧幀,避免棧增長。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved