从汇编的角度解析函数调用过程

看看下面这个简单函数的调用过程:

 int Add(int x,int y)
{
int sum = ;
sum = x + y;
return sum;
} int main ()
{
int a = ;
int b = ;
int ret = ;
ret = Add(a,b);
return ;
}

今天主要用汇编代码去讲述这个过程,首先介绍几个寄存器和简单的汇编指令的意思。 
先看几个函数调用过程涉及到的寄存器: 
(1)esp:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。 
(2)ebp:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。 
(3)eax 是”累加器”(accumulator), 它是很多加法乘法指令的缺省寄存器。 
(4)ebx 是”基地址”(base)寄存器, 在内存寻址时存放基地址。 
(5)ecx 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。 
(6)edx 则总是被用来放整数除法产生的余数。 
(7)esi/edi分别叫做”源/目标索引寄存器”(source/destination index),因为在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串. 
在32位平台上,ESP每次减少4字节。 
再看几条简单的汇编指令: 
mov :数据传送指令,也是最基本的编程指令,用于将一个数据从源地址传送到目标地址(寄存器间的数据传送本质上也是一样的) 
sub:减法指令 
lea:取偏移地址 
push:实现压入操作的指令是PUSH指令 
pop:实现弹出操作的指令 
call:用于保存当前指令的下一条指令并跳转到目标函数。 
这些指令当然能看懂最好,可以让你很深刻的理解函数调用过程,不能看懂就只能通过我的描述去理解了。 
进行分析之前,先来了解下内存地址空间的分布:

栈空间是向低地址增长的,主要是用来保存函数栈帧。 栈空间的大小很有限,仅有区区几MB大小 
汇编代码实现: 
main函数汇编代码:

int main ()
{
011B26E0 push ebp
011B26E1 mov ebp,esp
011B26E3 sub esp,0E4h
011B26E9 push ebx
011B26EA push esi
011B26EB push edi
011B26EC lea edi,[ebp-0E4h]
011B26F2 mov ecx,39h
011B26F7 mov eax,0CCCCCCCCh
011B26FC rep stos dword ptr es:[edi]
int a = ;
011B26FE mov dword ptr [a],0Ah
int b = ;
011B2705 mov dword ptr [b],0Ch
int ret = ;
011B270C mov dword ptr [ret],
ret = Add(a,b);
011B2713 mov eax,dword ptr [b]
011B2716 push eax
011B2717 mov ecx,dword ptr [a]
011B271A push ecx
011B271B call @ILT+(_Add) (11B1285h)
011B2720 add esp,
011B2723 mov dword ptr [ret],eax
return ;
011B2726 xor eax,eax
}
011B2728 pop edi
011B2729 pop esi
011B272A pop ebx
011B272B add esp,0E4h
011B2731 cmp ebp,esp
011B2733 call @ILT+(__RTC_CheckEsp) (11B11C7h)
011B2738 mov esp,ebp
011B273A pop ebp
011B273B ret

Add函数汇编代码:

int Add(int x,int y)
{
011B26A0 push ebp
011B26A1 mov ebp,esp
011B26A3 sub esp,0CCh
011B26A9 push ebx
011B26AA push esi
011B26AB push edi
011B26AC lea edi,[ebp-0CCh]
011B26B2 mov ecx,33h
011B26B7 mov eax,0CCCCCCCCh
011B26BC rep stos dword ptr es:[edi]
int sum = ;
011B26BE mov dword ptr [sum],
sum = x + y;
011B26C5 mov eax,dword ptr [x]
011B26C8 add eax,dword ptr [y]
011B26CB mov dword ptr [sum],eax
return sum;
011B26CE mov eax,dword ptr [sum]
}
011B26D1 pop edi
011B26D2 pop esi
011B26D3 pop ebx
011B26D4 mov esp,ebp
011B26D6 pop ebp
011B26D7 ret

下面图中详细描述了调用过程地址变化(此处所有地址是取自32位windows系统vs编辑器下的调试过程。):

过程描述: 
1、参数拷贝(参数实例化)。 
2、保存当前指令的下一条指令,并跳转到被调函数。 
这些操作均在main函数中进行。

接下来是调用Add函数并执行的一些操作,包括: 
1、移动ebp、esp形成新的栈帧结构。 
2、压栈(push)形成临时变量并执行相关操作。 
3、return一个值。 
这些操作在Add函数中进行。

被调函数完成相关操作后需返回到原函数中执行下一条指令,操作如下: 
1、出栈(pop)。 
2、回复main函数的栈帧结构。(pop ) 
3、返回main函数 
这些操作也在Add函数中进行。 至此,在main函数中调用Add函数的整个过程已经完成。 
总结起来整个过程就三步: 
1)根据调用的函数名找到函数入口; 
2)在栈中审请调用函数中的参数及函数体内定义的变量的内存空间 
3)函数执行完后,释放函数在栈中的审请的参数和变量的空间,最后返回值(如果有的话) 
如果你学了微机原理,你会想到cpu中断处理过程,是的,函数调用过程和中断处理过程一模一样。

函数调用约定: 
这里再补充一下各种调用规定的基本内容。 
_stdcall调用约定

所有参数按照从右到左压入堆栈,由被调用的子程序清理堆栈

_cdecl调用约定(The C default calling convention,C调用规定)

参数也是从右到左压入堆栈,但由调用者清理堆栈。

_fastcall调用约定

顾名思义,_fastcall的目的主要是为了更快的调用函数。它主要依靠寄存器传递参数,剩下的参数依然按照从右到左的顺序压入堆栈,并由被调用的子程序清理堆栈。

本篇博文是按调用约定__stdcall 调用函数。

csdn博客地址:http://blog.csdn.net/qq_38646470

C语言的函数调用过程的更多相关文章

  1. 从一个新手容易混淆的例子简单分析C语言中函数调用过程

    某天,王尼玛写了段C程序: #include <stdio.h> void input() { int i; ]; ; i < ; i++) { array[i] = i; } } ...

  2. 深入理解C语言的函数调用过程 【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-4240084.html 原文地址:深入理解C语言的函数调用过程 作者:wjlkoorey258     本文 ...

  3. 深入理解 C 语言的函数调用过程

    来源: wjlkoorey 链接:http://blog.chinaunix.net/uid-23069658-id-3981406.html 本文主要从进程栈空间的层面复习一下C语言中函数调用的具体 ...

  4. 深入理解C语言的函数调用过程

    本文主要从进程栈空间的层面复习一下C语言中函数调用的具体过程,以加深对一些基础知识的理解.     先看一个最简单的程序: 点击(此处)折叠或打开 /*test.c*/ #include stdio. ...

  5. C语言的函数调用过程(栈帧的创建与销毁)

    从汇编的角度解析函数调用过程 看看下面这个简单函数的调用过程: int Add(int x,int y) { ; sum = x + y; return sum; } int main () { ; ...

  6. C语言中函数调用过程(如何管理栈空间)

    ps:先做草稿,以后有时间再整理并贴图,:) 主要是利用栈底寄存器(ebp).栈顶寄存器(esp)跟eax寄存器(存储返回值)来实现. 假设P调用Q: P() { Q(1,2); } (跟实际情况可能 ...

  7. C语言函数调用过程,汇编角度查看

    C语言函数调用过程,汇编角度查看 把函数的参数按照调用约定压栈或者存储到寄存器中 调用要使用的函数,先把调用者的地址入栈,方便回来 跳转到函数 把函数使用到的一些寄存器压栈,避免修改寄存器的值 执行函 ...

  8. c语言中函数调用的本质从汇编角度分析

    今天下午写篇博客吧,分析分析c语言中函数调用的本质,首先我们知道c语言中函数的本质就是一段代码,但是给这段代码起了一个名字,这个名字就是他的的这段代码的开始地址 这也是函数名的本质,其实也就是汇编中的 ...

  9. C函数调用过程原理及函数栈帧分析(转)

    在x86的计算机系统中,内存空间中的栈主要用于保存函数的参数,返回值,返回地址,本地变量等.一切的函数调用都要将不同的数据.地址压入或者弹出栈.因此,为了更好地理解函数的调用,我们需要先来看看栈是怎么 ...

随机推荐

  1. HTML、CSS、JS 复习——序

    HTML在MVC中担任着M的角色.由HTML组成的节点称之为HTML DOM.要想成为一流的大神,就必须能分分钟建立起一个强大的HTML DOM.而要做到这点,就必须了解甚至精通HTML. CSS在M ...

  2. Intellij Idea中Backspace无法使用,Ctrl+c/Ctrl+d等等快捷键无法使用的问题的解决

    1:作为一个强迫症的我使用习惯了Eclipse,可能是对快捷键的依赖性,都说Idea是开发Java的最好工具,下载,安装等等(过程省略,百度很多方法),这里说一下我遇到的窘迫问题.哎,真是有时候看似天 ...

  3. C# 的基本数据类型

    bool System.Boolean 4Byte 32bit布尔型变量 逻辑值,true或者false,默认值为false byte System.Byte 1Byte 8bit无符号整数无符号的字 ...

  4. 软件安装之-------VM虚拟机安装windows系统

    一 准备工作 1 电脑已经安装上VMware Workstation 2 一个Windows系统,下载纯净版系统可到(www.itellyou.cn下载) 3  软碟通 下载可到(http://dow ...

  5. 使用 requirejs 打包 jQuery 插件 datetimepicker 的问题记录

    网站之前用的时间选择 UI 实在太丑,而且功能单一,决定全站改用 https://github.com/xdan/datetimepicker/ 里面有好几个 js,奇怪的是,只有 /build 目录 ...

  6. MATLAB数字图像处理学习笔记

    我们都知道一幅图片就相当于一个二维数组,可以用一个矩阵来表示,而MATLAB可以说就是为矩阵运算而生的,所以学习图像处理,学习MATLAB势在必行! 一. MATLAB基础知识 1. 读取图像 %im ...

  7. Django---->模板层(template)

    模板层(template) 你可能已经注意到我们在例子视图中返回文本的方式有点特别. 也就是说,HTML被直接硬编码在 Python代码之中. 1 2 3 4 def current_datetime ...

  8. python与ssh交互

    由于pexpect模块在windows上不能运行,官方给出的. 所以我使用了paramiko模块进行我们的交互. 我的linux是Debian Kali Linux 64位 首先我们要进vim的/et ...

  9. JavaSE(十一)之异常处理详解

    一.异常概述 在我们日常生活中,有时会出现各种各样的异常,例如:职工小王开车去上班,在正常情况下,小王会准时到达单位.但是天有不测风云,在小王去上班时,可能会遇到一些异常情况,比如小王的车子出了故障, ...

  10. HDU5752-Sqrt Bo

    Sqrt Bo Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total S ...