C语言的函数调用过程(栈帧的创建与销毁)
从汇编的角度解析函数调用过程
看看下面这个简单函数的调用过程:
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语言的函数调用过程(栈帧的创建与销毁)的更多相关文章
- C语言函数调用及栈帧结构
source:http://blog.csdn.net/qq_29403077/article/details/53205010 一.地址空间与物理内存 (1)地址空间与物理内存是两个完全不同的概念, ...
- 从一个新手容易混淆的例子简单分析C语言中函数调用过程
某天,王尼玛写了段C程序: #include <stdio.h> void input() { int i; ]; ; i < ; i++) { array[i] = i; } } ...
- 深入理解C语言的函数调用过程 【转】
转自:http://blog.chinaunix.net/uid-25909619-id-4240084.html 原文地址:深入理解C语言的函数调用过程 作者:wjlkoorey258 本文 ...
- 第6篇-Java方法新栈帧的创建
在 第2篇-JVM虚拟机这样来调用Java主类的main()方法 介绍JavaCalls::call_helper()函数的实现时提到过如下一句代码: address entry_point = me ...
- 深入理解 C 语言的函数调用过程
来源: wjlkoorey 链接:http://blog.chinaunix.net/uid-23069658-id-3981406.html 本文主要从进程栈空间的层面复习一下C语言中函数调用的具体 ...
- 深入理解C语言的函数调用过程
本文主要从进程栈空间的层面复习一下C语言中函数调用的具体过程,以加深对一些基础知识的理解. 先看一个最简单的程序: 点击(此处)折叠或打开 /*test.c*/ #include stdio. ...
- C语言的函数调用过程
从汇编的角度解析函数调用过程 看看下面这个简单函数的调用过程: int Add(int x,int y) { ; sum = x + y; return sum; } int main () { ; ...
- [Android Pro] 深入理解函数的调用过程——栈帧
cp :http://blog.csdn.net/x_perseverance/article/details/78897637 每一个函数被调用时,都会为函数开辟一块空间,这块空间就称为栈帧. 首先 ...
- 第3篇-CallStub新栈帧的创建
在前一篇文章 第2篇-JVM虚拟机这样来调用Java主类的main()方法 中我们介绍了在call_helper()函数中通过函数指针的方式调用了一个函数,如下: StubRoutines::cal ...
随机推荐
- appium 环境安装windows
创建AVD -c --sdcard : 指向一个共享的SD存储卡的路径,或者是新的SD储存卡容量大小. -n --name : AVD的名字(该项是必须的) -a --snapshot ...
- Autoware 安装流程
1. 前言 最近一个项目采用Autoware的无人驾驶系统,在安装工控机的过程中,踩了不少坑,留下记录,造福后人. 相关的下载在我的百度网盘,大家自行下载.链接: https://pan.baidu. ...
- PAT——1074. 宇宙无敌加法器(20)
地球人习惯使用十进制数,并且默认一个数字的每一位都是十进制的.而在PAT星人开挂的世界里,每个数字的每一位都是不同进制的,这种神奇的数字称为“PAT数”.每个PAT星人都必须熟记各位数字的进制表,例如 ...
- HDU 1829 A Bug's Life (种类并查集)
传送门: http://acm.hdu.edu.cn/showproblem.php?pid=1829 A Bug's Life Time Limit: 15000/5000 MS (Java/Oth ...
- myeclipse安装SVN插件方法
http://www.cnblogs.com/xdp-gacl/p/3497016.html myeclipse安装SVN插件方法 SVM China 源代码托管中心 http://www.svnch ...
- mysql 全量备份与增量备份
全量备份[root@master adm]# cat DBfullBak.sh #!/bin/bash #use mysqldump to fully backup mysql data BakDir ...
- 搭建springboot项目
1.搭建环境windows10+jdk1.8+eclipse4.8+maven 2.为了学习微服务架构学习搭建基础项目 3.分为两种搭建方式为maven项目和单独建立springboot项目(ecli ...
- Oracle 表空间、段、区和块简述
数据块(Block) 数据块Block是Oracle存储数据信息的最小单位.注意,这里说的是Oracle环境下的最小单位.Oracle也就是通过数据块来屏蔽不同操作系统存储结构的差异.无论是Windo ...
- mysql 5.7 或以上版本 group by 问题记录
mysql 5.7或以上的新版本sql_mode 默认开启开 ONLY_FULL_GROUP_BY,如果 select 中出现的字段,没有使用聚合函数,或不存在group by中就会提示,this i ...
- 用js实现导出功能将html中的table导出为excel
/** * 描述:导出表格对应的excel文件 * 时间:2018-03-29 * 作者:任恩远 * 调用示例: * onclick = "tableToExcel(tableId,file ...