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

C 語言返回值深度解析:從組合語言看呼叫慣例與結構體返回機制

     編輯:關於C語言

返回值看似簡單,實則暗藏玄機。以 Linux x86 為例,程式退出碼存於 eax,Shell 下 echo $? 即可查看。C 標準要求 main 返回 int,恰好與 32 位 eax 寬度一致。若用 void main(),編譯器可能報錯/警告,但若編譯通過,eax 殘留值即成返回碼——這解释了為何呼叫返回非零函數後 main 不顯式返回也能「帶回」該值。

返回值分類與寄存器映射(x86 32 位)

類型大小返回寄存器/方式備註
char1 Bal (低 8 位)符號擴展至 eax (movsbl)
short2 Bax (低 16 位)同上
int/long/指標4 Beax標準整數/指標返回
long long (64 位)8 Bedx:eaxedx 高 32 位,eax 低 32 位
float/double4/8 BFPU 堆疊頂 ST(0)即使運算結果在 eax,仍需 fld 壓入 FPU
struct任意隱藏參數:呼叫者分配空間,傳指標給被呼叫者關鍵機制,詳見下文

結構體返回的隱藏參數機制

GCC (System V ABI) 規定:呼叫者在堆疊壓入返回值緩衝區地址作為第一個隱藏參數,被呼叫者寫入該地址並將地址放回 eax 返回。即使結構體只有 1 byte 也同理。

範例:單字節結構體

struct xxx { char a; };
struct xxx f() { struct xxx x; x.a = '9'; return x; }
int main() { struct xxx y = f(); return 0; }

關鍵組合語言片段:

f:
    pushl %ebp
    movl  %esp, %ebp
    subl  $16, %esp
    movl  8(%ebp), %edx       ; 取出隱藏參數 (返回地址) → edx
    movb  $57, -1(%ebp)       ; '9' → 區域變數
    movzbl -1(%ebp), %eax     ; 擴展至 eax
    movb  %al, (%edx)         ; 寫入呼叫者緩衝區
    movl  %edx, %eax          ; 返回緩衝區地址
    leave
    ret $4                    ; 清理隱藏參數

main 調用前:leal -21(%ebp), %eax; movl %eax, (%esp) — 將區域變數 y 地址壓棧作為隱藏參數。

為何 struct xxx main() 會段錯?

main 入口由啟動程式碼呼叫,堆疊佈局非標準函數呼叫慣例。main 以為 8(%ebp) 有隱藏參數地址,實則為環境指標/參數計數等,寫入隨機地址導致段錯誤。

GCC 區塊表達式擴展

GCC 支援 a = ({ int b=2; int c=3; c-b; }) → a=1。規則:

  • 區塊必含非宣告語句,不可用 return。
  • 僅支援基本類型賦值,struct 不支援。
  • 取值邏輯:區塊結束時取 eax,按目標類型截取 (al/ax/eax/符號擴展/轉浮點)。

實際應用:Linux 核心 container_of 宏即利用此特性實現型別安全的容器指標計算。


本文出自「牛哥的博客」部落格,轉載請保留出處:原文連結

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