关于函数调用压栈和返回值问题的疑惑

按照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+1ch]对应的是eax,[esp+18h]对应的是z,[esp+10h]对应的是x,[esp+14h]对应的是y。再看下图

先把[esp+10h]的值也就是x的值赋给eax,再把[esp+14h]的值也就是y的值赋给edx,再分别把它们赋给[esp+4]和[esp]处,注意这里没用push指令压栈,但原理却是一样,因为用的是栈指针esp。所以这里就解释了先把y压栈,再把x压栈,确实是从右向左压栈

接下来再看add调用:

先取了y的值再取x的值,相加后结果保存在eax里。然后再回到main函数

调用完add后把eax的值赋值给了z,这就说明函数的返回值确实是保存在eax中。但为什么打印出来的eax却是0呢。

接着往下看,

首先把程序中eax变量的值赋给了eax寄存器,那当然就是0了。所以现在深入理解了C语言嵌入汇编的执行过程,就算指定了"+b"赋给ebx寄存器,但编译器还是会先把变量的值赋给eax寄存器,再赋值给ebx,返回也是一样的原理,如下图:

时间: 2024-10-06 07:50:58

关于函数调用压栈和返回值问题的疑惑的相关文章

汇编中call printf参数压栈时错误理解

EAX, ECX,EDX,EBX均可以32bit,16bit,8bit访问,如下所示: <-------------------EAX------------------------>|<----------------------|-----------|----------->|  |<---------AX--------->|  |<---AH--->|<---AL--->| 测试代码如下: 1 .section .data 2 outp

逆向知识十一讲,识别函数的调用约定,函数参数,函数返回值.

逆向知识十一讲,识别函数的调用约定,函数参数,函数返回值. 在反汇编中,我们常常的会看到各种的函数调用,或者通过逆向的手段,单独的使用这个函数,那么此时,我们就需要认识一下怎么识别函数了. 一丶识别__cdecl 函数(俗称C Call),函数参数,函数返回值 首先写一个C Call的函数 1.返回值 int类型, 参数int 类型 高级代码: int __cdecl MyAdd(int a,int b) { return a + b; } int main(int argc, char* ar

[转]WinExec、ShellExecute和CreateProcess及返回值判断方式

[转]WinExec.ShellExecute和CreateProcess及返回值判断方式 http://www.cnblogs.com/ziwuge/archive/2012/03/12/2392472.html 有三个API函数可以运行可执行文件WinExec.ShellExecute和CreateProcess.CreateProcess因为使用复杂,比较少用. WinExec主要运行EXE文件. ⑴ 函数原型: UINT Win Exec(LPCSTR lpCmdLine, UINT u

WinExec、ShellExecute和CreateProcess及返回值判断方式[转]

有三个API函数可以运行可执行文件WinExec.ShellExecute和CreateProcess.CreateProcess因为使用复杂,比较少用. WinExec主要运行EXE文件. ⑴ 函数原型: UINT Win Exec(LPCSTR lpCmdLine, UINT uCmdShow);  ⑵ 参数:  lpCmdLine:指向一个空结束的字符串,串中包含将要执行的应用程序的命令行(文件名加上可选参数).  uCmdShow:定义Windows应用程序的窗口如何显示,并为Creat

趣谈函数调用与返回值

今晚看老毕的视频时看到了函数的那块,视频中老毕写了一个算术方法,然后通过主函数调用它,方法得到参数后得到返回值再传给主函数.在提到标示返回值类型时,老毕说了一个很生动形象的例子 首先,来谈谈定义,当函数运算后,没有具体返回值时,这时返回值类型用一个特殊的关键字来标示,该关键字是void.当函数中的返回值类型是void时,函数中的return语句可以省略不写.好了,接下来谈谈老毕的那个形象的例子-----假设老毕是方法,同学是主函数,同学要买盒饭就要调用老毕,先给他钱,也就是“传参”,然后老毕返回

以函数返回值做参数时,函数调用的顺序

环境:vs2013 在下面的代码中 1 //类似于下面的代码 2 3 foo(char*,char*,char*); 4 5 char* str ="A#B#C"; 6 7 foo(strtok(str,"#"),strtok(NULL,"#"),strtok(NULL,"#")); 预计让函数foo得到("A","B","C")的参数,程序编译的时候没问题,但是运行

C语言函数调用参数压栈的相关问题

参数入栈的顺序 以前在面试中被人问到这样的问题,函数调用的时候,参数入栈的顺序是从左向右,还是从右向左.当时没有想清楚,随口就说从右向左.其实这个回答是不完全正确的.因为其实入栈的顺序,不同的体系架构是不一样的,举例来说, 看下面的代码: #include <stdio.h> int test(int a, int b) { printf("address of a %x.\n", &a); printf("address of b %x.\n"

JavaScript-4.3函数的参数和返回值,参数数目不确定的函数调用---ShinePans

<html> <head> <meta http-equiv="content-type" vontent="text/html;charset=GB2312"/> <title> 4.3 参数数目不明确的函数调用 </title> <!--脚本部分--> <script type="text/javascript"> function a(){ var str=

已定义的函数有返回值,函数调用可以作为一个函数的实参,但是不能作为形参

1.问题描述 若已定义的函数有返回值,则以下关于该函数调用的叙述中错误的是( D  ) A)函数调用可以作为独立的语句存在 B)函数调用可以作为一个函数的实参 C)函数调用可以出现在表达式中 D)函数调用可以作为一个函数的形参 解析:返回值存在寄存器中, 没有地址, 不能作为形参,但可以作为实参. 2.形参与实参区别 形参出现在函数定义中,在整个函数体内都可以使用, 离开该函数则不能使用. 实参出现在主调函数中,进入被调函数后,实参变量也不能使用. 形参和实参的功能是作数据传送.发生函数调用时,