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

C語言函數調用棧深度解析(二):調用約定、參數傳遞與返回值機制

     編輯:C 語言入門

函數調用約定核心要素

調用約定規範:參數壓棧順序、棧平衡責任方、名字修飾規則。不匹配將導致棧破壞、鏈接錯誤或運行時崩潰。

主流調用約定對比

約定參數順序棧清理可變參名字修飾典型場景
cdecl右→左調用者✓_funcC/C++ 默認
stdcall右→左被調用者✗_func@NWin32 API
fastcall前兩參寄存器(ECX/EDX),其餘右→左被調用者✗@func@N高性能調用
thiscall右→左,this 在 ECX被調用者(固定參)/調用者(可變參)視情況C++ 專用C++ 非靜態成員函數
naked自定義自定義自定義無內嵌彙編/裸機

cdecl 支援可變參數的根本原因

  1. 右→左壓棧:首參數最接近棧頂,偏移量固定(相對 EBP),不依賴參數總數。
  2. 調用者清棧:只有調用者知道實際傳遞了多少參數,能正確恢復 ESP。

若左→右壓棧,首參數偏移量需知參數總數,可變參無法定位;若被調用者清棧,無法知曉實際參數大小。

跨語言/跨平台調用建議

  • 導出庫函數統一用 stdcall(被調用者清棧,調用方無需關心參數大小)。
  • C++ 調用 C 庫:頭文件用 extern "C" 禁用 C++ 名字修飾。
  • GCC 屬性語法:__attribute__((stdcall));MSVC:__stdcall。

x86 參數傳遞細節(棧布局基於 EBP)

整型/指針(4 字節)

tail(1, 2, 3, (void*)0);
// 棧佈局(高地址→低地址)
[EBP+8]  = 1
[EBP+12] = 2
[EBP+16] = 3
[EBP+20] = 0

浮點型(8 字節,佔兩個棧槽)

tail(1.414, 2, 3.998e10);
// [EBP+8]  = 1.414 低 4 字節
// [EBP+12] = 1.414 高 4 字節
// [EBP+16] = 2
// [EBP+20] = 3.998e10 低
// [EBP+24] = 3.998e10 高

結構體/聯合體

  • 按值傳遞:整體壓棧,大小按 4 字節對齊補齊。
  • 大結構體建議傳指針,避免棧拷貝開銷。

返回值傳遞機制

類型大小返回方式
≤ 4 字節 (int, ptr, float)EAX
4~8 字節 (long long, double)EDX:EAX (高:低)
浮點 (float/double)x87 FPU 棧頂 ST(0)
結構體/聯合體隱式傳入返回地址指針作首參數 (foo(&ret, ...))

嚴重錯誤:返回棧地址

int* bad() { int x = 10; return &x; } // 錯誤!函數返回後棧幀失效
int* good() { static int x = 10; return &x; } // 正確:靜態存儲區
int* also_good(int *out) { *out = 10; return out; } // 正確:調用者提供存儲

GCC 結構體返回特殊規則

  • 1/2/4/8 字節 → 通用寄存器返回(同整數/浮點)。
  • 其他大小 → 調用者分配空間,傳入隱式指針。
  • 與 PCC(舊 Unix 編譯器)不兼容:PCC 用靜態緩衝區返回指針,不可重入。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved