函数栈帧的小知识理解

栈帧这是一块基础而又容易被忽略的知识

1.寄存器(独立的,集成到cpu上)

ebp esp 存放地址 维护函数栈帧的

每一个函数调用,都要在栈创建一个空间,就是由ebp和esp维护两端的,包括main函数

简单通过一段代码来解释一下过程

#include <stdio.h>

void foo(int a, int b) {
    int c = a + b;
    printf("c = %d\n", c);
}

int main() {
    int x = 5;
    int y = 10;
    foo(x, y);
    return 0;
}

这是一个简单的调用算出两数之和的程序 

首先我们进入main函数的时候,我们从vs的反汇编里去看

main:
    push    rbp
    mov     rbp, rsp
    sub     rsp, 16
    mov     DWORD PTR [rbp-4], 5
    mov     DWORD PTR [rbp-8], 10
    mov     eax, DWORD PTR [rbp-4]
    mov     esi, DWORD PTR [rbp-8]
    mov     edi, eax
    call    foo
    mov     eax, 0
    leave
    ret

首先我们push rbp的时候线保存我们main的指针

mov     rbp, rsp设置当前帧指针

这样,我们的rbp,rsp指针都指向了main函数的栈顶

在接下来的 sub     rsp, 16 这一步就是为我们的main函数开辟了16字节的空间

mov     DWORD PTR [rbp-4], 5 
mov     DWORD PTR [rbp-8], 10 

这两个move就把5和10压栈入我们的局部变量x和y中

  • mov eax, DWORD PTR [rbp-4]
  • mov esi, DWORD PTR [rbp-8]
  • mov edi,eax

这是将我们的变量加载到寄存器中

call foo:调用 foo 函数


接下来的foo函数里面

  • push rbp
  • mov rbp, rsp
  • sub rsp, 16

这里和main函数大同小异,都是保存指针,开辟栈帧空间

然后是把变量存储起来放入寄存器中计算,得出结果并且打印

  • leave
  • ret

恢复调用者的帧指针,返回到调用者,把开辟的栈帧空间销毁

由于开辟地址是由高到低的,所以我们会发现如果下面我们在重复一次调用foo函数,里面变量的地址也是不变的

最后加上一点寄存器的总结

总结

  • eax/rax:用于存储函数的返回值。
  • edi/rdiesi/rsi:在 System V AMD64 ABI 调用约定中,用于传递函数的第一个和第二个参数。
  • rcxrdx:在 Microsoft x64 calling convention 调用约定中,用于传递函数的第一个和第二个参数。

理解好函数栈帧的调用对于我们的理解以及计算空间复杂度都是很有利的,多多尝试理解会有好处

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值