函数栈帧的创建和销毁(代码片段)

北川_ 北川_     2022-12-09     512

关键词:

目录

各种寄存器的作用

eax是“累加器”(accumulator),它是很多加法乘法指令的缺省寄存器
ebx是“基地址”(base)寄存器,在内存寻址时存放基地址
ecx是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。
edx:总是被用来放整数除法产生的余数。
esp:寄存器存放当前线程的栈顶指针
ebp:寄存器存放当前线程的栈底指针

main()函数的调用

VS2013中mainCRTStartup()函数内部调用__tmainCRTStartup()函数,
__tmainCRTStartup()函数内部调用main()函数

通过汇编观察函数调用过程

int Add(int x, int y)

	int z = 0;
	z = x + y;
	return z;


int main(void)

	int a = 10;
	int b = 20;
	int c = 0;
	c = Add(a, b);
	printf("%d\\n", c);
	return 0;

汇编代码

int main(void)

00301900  push        ebp  
00301901  mov         ebp,esp  
00301903  sub         esp,0E4h  
00301909  push        ebx  
0030190A  push        esi  
0030190B  push        edi  
0030190C  lea         edi,[ebp+FFFFFF1Ch]  
00301912  mov         ecx,39h  
00301917  mov         eax,0CCCCCCCCh  
0030191C  rep stos    dword ptr es:[edi]  
0030191E  mov         ecx,30C003h  
00301923  call        0030132A  
	int a = 10;
00301928  mov         dword ptr [ebp-8],0Ah  
	int b = 20;
0030192F  mov         dword ptr [ebp-14h],14h  
	int c = 0;
00301936  mov         dword ptr [ebp-20h],0  
	c = Add(a, b);
0030193D  mov         eax,dword ptr [ebp-14h]  
00301940  push        eax  
00301941  mov         ecx,dword ptr [ebp-8]  
00301944  push        ecx  
00301945  call        003010B9  
0030194A  add         esp,8  
0030194D  mov         dword ptr [ebp-20h],eax  
	printf("%d\\n", c);
00301950  mov         eax,dword ptr [ebp-20h]  
00301953  push        eax  
00301954  push        307B30h  
00301959  call        003010D7  
0030195E  add         esp,8  
	return 0;
00301961  xor         eax,eax  

上面提到main函数也是被调用的,所以当代码走到main函数中时,调用main函数的函数的栈帧已经被创建好了。

main()函数栈帧开辟过程

第3行把ebp压栈,第4行把esp的值赋给ebp,第5行esp减去0E4h,此时esp和ebp之间就是main函数的栈帧

6-8行把ebx、esi、edi压栈

lea表示load effective address加载有效地址
第9行到第12行将main函数栈帧的内容全部初始化位cccccccc

此时main函数栈帧开辟好了。
第16行将ebp-8的位置初始化为10,这是变量a的空间,18行将ebp-14h的位置赋值给20,为变量b,20行ebp-20h的位置赋值为0,为变量c

接下来第22行把ebp-14h的值(也就是b的20)赋给eax,并把eax压栈,把ebp-8的值(也就是a的10)赋给ecx,把ecx压栈。然后第26行call指令调用Add()函数,在调用call指令时会把call指令的下一条指令的地址压栈

Add()函数栈帧开辟过程

接下来调用Add()函数
Add()函数的汇编

int Add(int x, int y)

00301790  push        ebp  
00301791  mov         ebp,esp  
00301793  sub         esp,0CCh  
00301799  push        ebx  
0030179A  push        esi  
0030179B  push        edi  
0030179C  lea         edi,[ebp+FFFFFF34h]  
003017A2  mov         ecx,33h  
003017A7  mov         eax,0CCCCCCCCh  
003017AC  rep stos    dword ptr es:[edi]  
003017AE  mov         ecx,30C003h  
003017B3  call        0030132A  
	int z = 0;
003017B8  mov         dword ptr [ebp-8],0  
	z = x + y;
003017BF  mov         eax,dword ptr [ebp+8]  
003017C2  add         eax,dword ptr [ebp+0Ch]  
003017C5  mov         dword ptr [ebp-8],eax  
	return z;
003017C8  mov         eax,dword ptr [ebp-8]  

003017CB  pop         edi  
003017CC  pop         esi  
003017CD  pop         ebx  
003017CE  add         esp,0CCh  
003017D4  cmp         ebp,esp  
003017D6  call        0030124E  
003017DB  mov         esp,ebp  
003017DD  pop         ebp  
003017DE  ret

从第3行开始的一系列操作和上面的main函数中一样,依然是为函数开辟栈帧,并且把Add()函数栈帧空间初始化为cccccccc。

第16行把ebp-8的位置赋值为0,作为变量z的空间,18行把ebp+8位置的值放到eax中,19行把ebp+12的位置的值放到eax中,完成了两个变量的相加
第20行把eax中的30放到ebp-8也就是变量z的空间

return z,局部变量z在函数执行结束后销毁了,所以第22行的意思是把放在z变量中的两个数求和的结果保存在eax中。

Add()函数栈帧销毁过程

第24-26行把edi、esi、ebx出栈,然后把ebp赋给esp,pop掉ebp让ebp重新指向main函数栈底的位置,ret返回到之前保存的call指令的下一条指令的地址,自此Add函数调用结束,继续执行main函数
返回main函数后形参x和形参y没用了,把他们也出栈
过程如下:

回到main()函数后把eax中保存的30赋给ebp-20也就是c,此时完成了Add()函数栈帧的创建,传递形参,求值并保存在eax中,Add函数销毁,将函数返回值赋给变量c的一系列操作。

函数栈帧的创建和销毁(代码片段)

目录各种寄存器的作用main()函数的调用通过汇编观察函数调用过程main()函数栈帧开辟过程Add()函数栈帧开辟过程Add()函数栈帧销毁过程各种寄存器的作用eax是“累加器”(accumulator),它是很多加法乘法指令的缺省寄存器ebx是“... 查看详情

函数栈帧的创建与销毁(代码片段)

文章目录1.函数栈帧的概念2.函数栈帧的创建2.1main函数函数栈帧的创建过程2.2main函数中创建变量2.3Add函数函数栈帧的创建2.4Add函数栈帧的销毁1.函数栈帧的概念函数栈帧:使用每一个函数都要在栈区开辟一块空间.栈帧也叫过程活... 查看详情

函数栈帧的创建和销毁(代码片段)

...的说明二、对于创建和销毁的全过程1.对于_mainCRTstarup的函数的创建2.对于main函数的创建(1).为什么有时候会打印出烫烫烫3.对于Add的函数的创建(2).为什么说形参不在函数中(3).函数中return值如何放回的>(4).ebp-main出栈后ebp寄存器... 查看详情

函数栈帧的创建和销毁(待写)(代码片段)

函数栈帧的创建和销毁main函数被调用的过程:具体过程main函数被调用的过程:mainCRTStartup()调用_tmainCRTStartup()再调用main()寄存器:ebp(栈底指针),esp(栈顶指针)(sp是esp的低16位,esp是rsp的低32位,ss是16位堆栈... 查看详情

函数栈帧的创建与销毁(代码片段)

...什么是栈帧C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈... 查看详情

函数栈帧的创建与销毁(代码片段)

文章目录1.函数栈帧的概念2.函数栈帧的创建2.1main函数函数栈帧的创建过程2.2main函数中创建变量2.3Add函数函数栈帧的创建2.4Add函数栈帧的销毁1.函数栈帧的概念函数栈帧:使用每一个函数都要在栈区开辟一块空间.栈帧也叫过程活... 查看详情

图解函数栈帧-函数的创建与销毁(代码片段)

函数栈帧🎂前言🌹栈帧的概念💖准备工作😀main函数栈帧的创建及初始化😁main函数的被调用😂main函数栈帧的开辟🤣main函数栈帧的初始化👩临时变量的创建。👨Add函数栈帧的创建🧑Add... 查看详情

c语言深入逐汇编详解函数栈帧的创建和销毁过程(代码片段)

【C语言深入】逐汇编详解函数栈帧的创建和销毁过程一、图解大概过程二、函数栈帧的创建过程1、简介一些需要用到的汇编指令和寄存器2、调用main函数的函数3、局部变量的初始化4、形成临时拷贝5、函数调用6、形成栈帧7、提... 查看详情

函数栈帧的创建与销毁,带你了解代码底层原理(代码片段)

...么创建的?(2)为什么局部变量的值是随机的?(3)函数是怎么传参的?传参的顺序如何?(4)形参和实参是什么关系?(5)函数调用是怎么做的?(6)函数调用结束后是怎么返回的?这些疑问其实都和函数栈帧... 查看详情

内功修炼《函数栈帧的创建和销毁》建议收藏(代码片段)

...️⃣为什么未初始化的局部变量的值是随机值?3️⃣函数是如何传参的?以及传参的顺序是怎样的?4️⃣形参和实参是什么关系?5️⃣ 查看详情

c语言学习--函数栈帧的创建和销毁(代码片段)

...说局部变量未初始化时,其中存储的时随机值?函数到底时如何传参的?实参传递的顺序又是怎样的?形参和实参之间有着什么关系?函数调用结束后,结果是如何返回的?这些问题大 查看详情

函数栈帧的创建和销毁——“c”(代码片段)

...你们好呀,今天小雅兰来为大家介绍一个知识点——函数栈帧的创建和销毁。其实这个知识点,我们很早之前就要讲,但是因为我的一系列原因,才一直拖到了现在,那么,话不多说,让我们一起进入... 查看详情

图解c/c++语言底层:函数调用过程之函数栈帧的创建和销毁(上)(代码片段)

**文章目录函数栈帧的创建和销毁什么是寄存器?寄存器分类寄存器用途什么是"栈"?函数栈帧的概念函数压栈的过程示例代码和主函数汇编指令(部分)汇编指令:构建函数栈帧准备(一)汇编指令:构建函数栈... 查看详情

函数的调用过程,栈帧的创建和销毁。

一.函数调用1.函数调用过程涉及到的寄存器: (1)esp:栈指针寄存器(extendedstackpointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。 (2)ebp:基址指针寄存器(extendedbasepointer),其内存放着一个... 查看详情

c语言函数栈帧的创建和销毁,以简单函数的调用来进行详细刨析(代码片段)

1、为什么需要了解函数的栈帧大家对于程序很少有人会对底层的东西刨根问底,很多人写程序大多就是写出来能够成功运行起来就大无所措了,但是很少有人对程序是怎么跑起来较为关心,比如:局部变量是怎么... 查看详情

图解c/c++底层:函数栈帧的创建和销毁(下篇)

函数栈帧的创建和销毁(下篇)上篇原文链接根据上篇的函数栈帧过程的学习,我们了解到:什么是寄存器?计算机的速度最快的存储单元,因为寄存器是集成在CPU之上的,与内存是不同的独立的存储空间。什么... 查看详情

学好c语言,还需要掌握这个内功——函数栈帧的创建与销毁(代码片段)

...是怎么创建的?为什么局部变量的值是随机值?函数是怎么传参的?传参的顺序是什么?形参和实参是什么关系?函数调用结束后怎么返回?看完这篇文章,一切将豁然开朗……预备知识在进入正题之... 查看详情

内功修炼《函数栈帧的创建和销毁》建议收藏(代码片段)

...️⃣为什么未初始化的局部变量的值是随机值?3️⃣函数是如何传参的?以及传参的顺序是怎样的?4️⃣形参和实参是什么关系?5️⃣函数调用是怎么做的?6️⃣函数调用结束后是怎么返回的?⚠这里... 查看详情