C语言函数栈帧
保护小周ღ 人气:0前言
C语言,我们学到了这里,一定会有许多疑惑,我们的代码逻辑,定义的变量,自定义的函数等通过编译器,准确的来讲是集成开发环境(vs),预处理、编译、汇编、链接,在电脑上执行的过程中,计算机内部到底是怎么运行的,我们要搞清楚这些基础的过程,而不是说,因为这样做,所以有什么样的结果,过程是怎么样的,我们一概不了解,这样学编程是不对的,要刨根问底,增强自己的内功。保姆级讲述,包您一看就会,快来试试吧~
举个例子:
int main() { int a=0; return 0; }
根据以上代码,我们可以读出一段信息,定义一个局部整型变量 a,并将他初始化为0,同时一个整型变量在内存中占4个字节。关于main()函数的返回值啊,简单说一下,如果返回值为0,则说明程序是正常结束,非0,就是不正常结束,且返回值为整型数据。
那么博主在这里问大家两个问题:
- 局部整型变量a,到底是怎么创建的?
- 如果我们对变量a不进行初始化操作,局部变量a的值是多少?
- main()函数也有返回值,那他是不是也是也被其他函数调用?
函数在调用的过程中栈帧的创建和销毁对于不同的编译器来说略有差异,但大体的逻辑是差不多的。取决于编译器。
什么是函数栈帧?
每一次函数的调用,操作系统都会在内存的栈区上开辟一块空间,称为栈帧。
函数调用建立栈帧,栈帧中存储局部变量,参数等等。
栈区,堆区等是操作系统这门学科中对内存的划分,数据结构的“栈”,“堆”是存放、处理数据的一种结构,跟内存的栈区,堆区,没有啥关系,但是有一点,数据结构的“栈”和内存的栈区都是后进先出,先进后出的特性。
举个例子:
在main() 函数内部的Sum()函数则是被 main()函数调用,Sum()函数结束开辟的栈帧被操作系统回收,返回mian()函数的Sum()函数调用点,继续执行程序。
程序顺序执行 ,执行main()函数,遇到 int a=1; 操作系统就会在内存的栈区上,为main()函数创建栈帧里,给局部变量 a 分配一块4个字节的空间。同理,局部变量在那个函数里创建的,那么就在那个函数的栈帧中为其分配空间。函数在调用的时候操作系统会为函数预划分一片空间,就是所谓的栈帧,由 esp,ebp 两个寄存器维护,如果空间不够了,操作系统会在这片空间的栈顶(esp)增加空间入栈,esp 可以动态的维护栈顶。
知识点普及:
局部变量是在内存的栈区上开辟的,栈区内存的使用习惯是先使用高地址的空间,后使用低地址的空间,栈区又是一种先进后出,后进先出的结构,且只能在一端操作。
在为函数创建栈帧的时候,会有很多的寄存器去维护,这一片的可使用空间就会被初始化为随机值,这个根据不同的编译器有不同的初始化方式,这就是我们对局部变量定义的时候不进行初始化打印随机值的原因。
由图可见,栈底寄存器 ebp-0E4h 就等于 edi 指向的空间。
不过现在的编译器很智能,如果没有初始化局部变量使用的话会报错,就很强,但是大家还是要养成良好的初始化的习惯。
Sum() 函数的调用
传参:
把变量a,b 的值分别存储在 ecx ,eax 寄存器,分别入栈。就相当于使用两块空间 又将a,b 存储了一次。这就是形参嘛。
形参是实参的拷贝!这段形参开辟的空间实际上还是在main() 函数的栈帧中。esp 栈顶指针动态维护。
esp ,edp 如何从main() 栈帧,来维护 Sum() 函数栈帧:
区间实际上是(ebx,ebp)
return 会返回 call 指令的这个地址,可以继续执行程序。
C/C++ 程序内存分配的几个区域:
1.栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限,栈区主要存放运行函数而分配的局部变量,函数参数,返回数据,返回地址等。
2.堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时由OS回收。
3.数据段(静态区)(static)存放全局变量,静态变量。程序结束后由系统释放。
4.代码段:存放函数体(类成员函数和全局函数)的二进制代码
至此C语言函数栈帧的创建和销毁博主已经分享完了,相信大家对这个函数怎么建立的、销毁的,函数怎么传参,使用等有了一定的理解,大家可以自己使用汇编代码,感受一下。
总结
加载全部内容