深入解析C++函数栈帧与寄存器工作原理:VS2019调试工具实战指南
本文使用VS2019的代码调试工具来分析C++函数堆栈框架;

本文继续《C++的异常处理机制》,分析异常捕获的原理。接下来我们看一下栈帧的工作原理。你也会明白为什么栈内存可以自动释放,而堆内存需要通过delete释放。
1. 关于寄存器
寄存器--32位程序调试
通用寄存器:主要用于存储操作数、运算结果等信息,从而节省读取操作数时占用总线和访问内存所需的时间。 RAX、RBX、RCX、RDX 和 EAX、EBX、ECX、EDX 和 AX、BX、CX 和 DX 分别称为 64 位、32 位和 16 位通用寄存器。并增加8个通用寄存器(R8~R15)。有四个通用寄存器(RAX、RBX、RCX、RDX),它们的低32位与原来的32位通用寄存器(EAX、EBX、ECX、EDX)重叠并共享。
指针寄存器:指针寄存器主要用于存储存储单元在栈中的偏移量。它们可用于实现内存操作数的多种寻址模式,为以不同地址形式访问存储单元提供便利。 RIP、RBP、RSP、RSI、RDI 和 EIP、EBP、ESP、ESI、EDI 和 IP、BP、SP、SI 和 DI 分别称为 64 位、32 位和 16 位指针寄存器。
索引寄存器:主要用于存储存储单元在段内的偏移量。它们可用于实现内存操作数的多种寻址模式,为以不同地址形式访问存储单元提供便利。
此外,还有段寄存器、指令指针寄存器、标志寄存器等。
这里我们只讲EBP和ESP寄存器:
2.关于栈帧
栈帧的主要作用是控制和保存函数调用的所有信息。它在调用函数时为进程在堆栈空间中开辟一块空间来执行过程调用。过程调用包括传输数据(以过程参数和返回值的形式),并将控制从代码的一个部分传递到另一部分。此外,它必须在进入时为过程的局部变量分配空间,并在退出时释放它们。
栈帧构造完成后,会有两个寄存器来标识栈帧,分别是基址寄存器EBP(指向栈底)和栈顶寄存器ESP(指向栈顶)。这两个寄存器存储栈顶和栈底。地址,访问栈帧中的一些信息也是通过这两个指针寄存器。通常EBP是不动的,压栈和出栈操作是通过ESP的移动来实现的。
请看示例代码:
int add(int x,int y)
{
return x + y;
};
int main()
{
int a = 1;
int b = 2;
auto c = add(a, b);
return 1;
}
下图是add函数的完整组装
添加功能组件
从上图可以看到,栈帧是在执行add函数的左大括号时创建的(add的第一行还没有执行完)。接下来我们来分析一下add函数的栈帧结构。首先,简单翻译一下汇编语法。获得了添加栈帧的创建和推送过程。
根据上图的翻译,可以得到如下所示的add函数栈帧。
函数栈帧
我们再看一下栈帧(执行add函数的右大括号开始弹出栈帧)。根据后进先出原则,看mov esp、ebp和pop ebp指令。
如下图所示,esp和ebp可以重定向到main函数栈帧。
恢复esp和ebp再次指向main函数栈帧