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

C語言函數調用棧深度解析(一):棧幀結構、寄存器約定與調用流程

     編輯:C 語言入門

調用棧核心概念

每個用戶態進程擁有一個調用棧,函數調用即棧幀的壓入/彈出。棧幀包含:參數、返回地址、舊幀基址 (EBP)、局部變量、被調用者保存寄存器。

IA32 關鍵寄存器

寄存器角色調用約定分類
EAX返回值、乘除隱式操作數調用者保存
ECX, EDX通用、fastcall 參數傳遞調用者保存
EBX, ESI, EDI通用被調用者保存
EBP幀基址指針被調用者保存(必須恢復)
ESP棧頂指針被調用者保存(必須恢復)
EIP指令指針隱式修改

棧幀標準佈局(高地址 → 低地址)


+-------------------+  ← 高地址 (棧底)
| 參數 N ... 參數 1 |  調用者壓入 (右→左)
+-------------------+
| 返回地址 (EIP)    |  CALL 隱式壓入
+-------------------+
| 舊 EBP            |  被調用者壓入
+-------------------+  ← EBP (幀基址)
| 被調用者保存寄存器|  可選 (EBX/ESI/EDI)
+-------------------+
| 局部變量區        |  SUB ESP 分配
| ...               |
+-------------------+  ← ESP (棧頂, 低地址)

函數序與跋

; 序言
push ebp            ; 保存舊幀基址
mov  ebp, esp       ; 建立新幀基址
sub  esp, N         ; 分配局部變量空間 (N 通常 16 字節對齊)
push ebx/esi/edi    ; 保存被調用者保存寄存器

; 函數體 ...

; 跋語
pop  edi/esi/ebx    ; 恢復寄存器
mov  esp, ebp       ; 釋放局部變量區
pop  ebp            ; 恢復舊幀基址
ret                 ; 彈出返回地址跳轉
; leave 等價於 mov esp,ebp / pop ebp

參數傳遞兩種等效指令序列

; 方式一:逐個 PUSH
push 4
push 3
push 2
push 1
call func
add  esp, 16        ; 調用者清棧

; 方式二:預分配棧空間 + MOV (GCC ≥ 3.4 默認)
sub  esp, 16
mov  [esp+12], 4
mov  [esp+8],  3
mov  [esp+4],  2
mov  [esp],    1
call func
; 無需 ADD ESP,CALLEE 序言已處理

關鍵操作指令語義

  • PUSH reg:ESP -= 4; [ESP] = reg
  • POP reg:reg = [ESP]; ESP += 4
  • CALL addr:PUSH EIP; JMP addr
  • RET [imm]:POP EIP; ESP += imm (stdcall 清棧)
  • LEAVE:MOV ESP, EBP; POP EBP

局部變量佈局不確定性

編譯器版本、優化等級、架構均影響局部變量相對位置。若需固定相對佈局,必須用結構體/數組。

實戰驗證代碼片段

void callee(int a, int b, int c) {
    int x = 1, y = 2, z = 3;
    printf("a@%p b@%p c@%p\n", &a, &b, &c);
    printf("x@%p y@%p z@%p\n", &x, &y, &z);
}

int main() {
    int p=10,q=20,r=30;
    callee(p,q,r);
    return 0;
}

觀察輸出地址關係,驗證參數正偏移、局部變量負偏移。

多線程棧切換

線程切換本質是修改 ESP 指向目標線程棧頂,配合 EIP/EBP 實現上下文切換。

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