「參數從右向左壓棧,返回值放在 EAX」——這是 x86 C 調用約定(cdecl)的教科書定論。但為什麼內嵌匯編讀取 EAX 卻得到 0?本文通過反匯編一步步還原真相。
#include <stdio.h>
int add(int x, int y) { return x + y; }
int main() {
int eax = 0, z = 0, x = 6, y = 5;
z = add(x, y);
__asm__("movl %%eax, %0" : "=r"(eax) : "m"(x));
printf("z = %d, eax = %d\n", z, eax);
return 0;
}
輸出:z = 11, eax = 0 —— 明明 add 返回 11,為何 eax 是 0?
push 5; push 6; call add —— 確認從右向左壓棧。mov eax, [ebp+8]; add eax, [ebp+12] —— 結果確實存在 EAX 中。mov [z], eax —— 返回值正確賦給 z。mov eax, [ebp-4] ; 這裡的 eax 是局部變量 eax 的地址!GCC 內嵌匯編的 "=r"(eax) 輸出約束,會將輸出操作數綁定到通用寄存器(通常是 EAX)。編譯器生成代碼:
movl %eax, -4(%ebp) ; 先把局部變量 eax 的值(0)存入 EAX ; ... 你的 movl %%eax, %0 其實是在讀取這個剛被覆蓋的 EAX
所以:內嵌匯編的輸出約束會覆蓋函數返回殘留在 EAX 中的值!
z = add(x,y); —— 最簡單、最可移植。call 指令緊接著讀取 EAX,不要插入任何 C 語句:__asm__(
"call add\n\t"
"movl %%eax, %0"
: "=r"(eax)
: "m"(x), "m"(y)
);