浅谈栈区

栈区:

是用于存储函数调用和局部变量的一块内存区域。它的大小取决于编译器的设置和编译器选项。在大多数编译器中,栈区的默认大小是固定的,通常在几MB到几十MB之间。这个默认大小可以通过编译器选项进行修改。

栈区用来存储以下信息:

  1. 函数参数:函数参数是通过栈区来传递的,当函数被调用时,函数的参数会被按照一定的顺序依次压入栈中。

  2. 局部变量:函数中定义的局部变量和临时变量也是存储在栈区中的。当函数被调用时,这些变量会被分配一定的内存空间,并在函数执行结束后自动释放。

  3. 返回地址:函数调用过程中,函数的返回地址会被存储在栈区中,以便在函数执行结束后返回到正确的调用位置继续执行。

  4. 栈帧指针:栈帧指针(Frame Pointer)是一个记录当前栈帧位置的指针,它指向当前函数开头处的栈地址。用于在程序执行过程中能够准确地获取局部变量的内存位置。

     用以下代码做个简单示例:

int test(int x ,int y) 
{
	return 0;
}
int main() 
{
	int  a = 120;
	int  b = 12;
	test(a, b);
    return 0;
}

在栈区内部情况大致是这样:

第一步:栈区开辟main函数的栈帧,再用两个寄存器esp和edp分别指向当前栈帧的栈顶和栈帧的起始位置确定一片栈帧大小,当前创建的main函数栈帧所以维护的是main函数
  1. ESP (Stack Pointer):

    • ESP 是栈指针寄存器,它指向当前栈的栈顶位置。
    • 在函数调用时,ESP 用于分配和释放栈空间。
    • 当函数被调用时,ESP 会向下移动,为局部变量和参数分配空间;函数返回时,ESP 会向上移动,释放函数调用时分配的栈空间。(因为栈区使用习惯是从高地址到低地址,所以向下是开辟)
  2. EBP (Base Pointer):

    • EBP 是基址指针寄存器,通常用于指向当前函数栈帧的基址或起始位置。
    • 在函数内部,EBP 的值常用于访问函数参数和局部变量(加偏移量方便访问栈帧内部的局部变量以及其他一些东西)。
    • 通过 EBP,函数可以在栈上更方便地定位和访问自己的局部数据,而不受栈指针移动的影响。
    • 在函数调用时,EBP 会被保存到栈上,以便在函数返回时能够恢复原始的 EBP 值。
 第二步:main栈帧里面开辟两个变量a,b所需的内存空间

浅谈栈区_第1张图片

第三步:当main函数调用test 函数时,开始压栈,创建test函数的栈帧同时esp和edp指向test函数栈帧的起始和栈顶

浅谈栈区_第2张图片

第四步:在里面开辟形参的所需内存以及main函数返回地址方便返回

以上是函数调用在栈中的情况

test 函数执行完成后,程序需要进行函数返回的过程,将控制流程返回到调用函数(例如 main 函数)的正确位置。以下是函数返回的一般过程:

  1. 恢复栈帧:

    • test 函数执行完成后,程序会从 test 函数的栈帧中取出保存的返回地址,这个地址指向调用 test 函数的函数(比如 main 函数)的下一条指令。
  2. 栈帧指针(EBP)的恢复:

    • 如果在 test 函数调用前保存了 main 函数的栈帧指针 EBP,则需要将 EBP 的值恢复为先前保存的值。这是为了确保栈帧的正确操作。
  3. 栈指针(ESP)的调整:

    • ESP 在函数调用时会向下移动,为局部变量和参数分配栈空间。在函数返回时,ESP 需要向上移动,释放先前分配的栈空间。
  4. 跳转到返回地址:

    • 将控制流程跳转到保存的返回地址,这样程序就会返回到调用 test 函数的函数的下一条指令处。

使用栈区好处:先进后出的结构方便随用随丢(执行完当前函数如果没有再调用其他函数就销毁)以及栈区是线性回归方便寄存器的实现,栈区的内存管理相对简单,不需要手动分配和释放内存。这减少了程序员的负担,降低了出错的可能性。

你可能感兴趣的:(数据结构)