關於C語言函數調用壓棧和返回值問題的疑惑


按照C編譯器的約定調用函數時壓棧的順序是從右向左,並且返回值是保存在eax寄存器當中。這個命題本該是成立的,下面用一個小程序來反匯編觀察執行過程:

#include<stdio.h>

int add(int x, int y){
	return x+y;
}

int main(){
	int eax=0;
	int z =0;
	int x =6;
	int y =5;

	z=add(x,y);

	__asm__(
					"movl %%eax, %0"
				:"+b"(eax)
				:"m"(x)
					);

	printf("z is %d\n", z);
	printf("eax is %d\n", eax);
	return 0;
}

代碼解釋一下,asm的代碼中movl %%eax, %0的意思是把寄存器eax的值賦值給咱們程序的eax變量當中。但為什么執行結果卻是:

z is 11
eax is 0

理論上應該是x和y相加返回的結果才對啊。反匯編一下此exe程序:

上面是main函數,看下圖

esp自減了20h,說明開辟了20h也就是32字節的棧空間,再看下圖:

[esp+1ch]對應的是程序中的變量eax,也就是把eax壓在了esp+28處,此變量是int型4個字節,所以剛好對應的是棧底元素;[esp+18h]對應的是z也就是esp+24處,[esp+10h]對應的是x也就是esp+16處,[esp+14h]對應的是y。再看下圖

先把[esp+10h]的值也就是x的值賦給eax,再把[esp+14h]的值也就是y的值賦給edx,再分別把它們賦給[esp+4]和[esp]處,注意這里沒用push指令壓棧,但原理卻是一樣,因為用的是棧指針esp,還需要注意的是因為不是使用push指令,所以不是說誰先執行誰就先壓棧,而是觀察esp指向的位置來確定壓棧的先后順序,因為[esp]指向的是棧頂元素。所以這里就解釋了先把y壓棧,再把x壓棧,確實是從右向左壓棧

接下來再看add調用:

注意上面call add指令會先把eip壓棧,相當於esp=esp-4,並且這里還執行了push ebp指令,所以esp又自減了4,那么x的值就不再是入棧時候的[esp]了,而是[esp+8],所以y的值也不再是[esp+4]而是[esp+12],所以這里出棧的時候也不是看執行的先后順序,而是x本身就處於棧頂,相加后結果保存在eax里。然后再回到main函數

調用完add后把eax的值賦值給了z,這就說明函數的返回值確實是保存在eax中。但為什么打印出來的eax卻是0呢。

接着往下看,

首先把程序中eax變量的值賦給了eax寄存器,那當然就是0了。所以現在深入理解了C語言嵌入匯編的執行過程,就算指定了"+b"賦給ebx寄存器,但編譯器還是會先把變量的值賦給eax寄存器,再賦值給ebx,返回也是一樣的原理,如下圖:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM