调用函数时栈中信息的变化(C语言)
用栈来支持过程的 嵌套调用,过程的入口参数、返回地址、被保存寄存器的值、被调用过程中的非静态局部变量等都会被压入栈中。
在代码执行过程中,每一个过程都会有自己的栈区,称为栈帧,一个栈由若干栈帧组成,每个栈帧用专门的 栈帧寄存器(EBP)指定起始位置。因此,当前栈帧的范围在栈指针 EBP和栈指针 ESP指向区域之间。
栈
栈的地址是从高地址向低地址增长,栈底指向低地址,栈顶指向高地址,栈中每弹出一个值,栈顶指针向高地址移动,如果存储一个int型数据,则栈顶地址加4,每压栈一次,栈顶指针-4。
此处有误,中间是相差16个字节
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jM9JTz5f-1668615005742)(%E8%B0%83%E7%94%A8%E5%87%BD%E6%95%B0%E6%97%B6%E6%A0%88%E4%B8%AD%E4%BF%A1%E6%81%AF%E7%9A%84%E5%8F%98%E5%8C%96%EF%BC%88C%E8%AF%AD%E8%A8%80%EF%BC%89%2053aad5c3f5194570ac32ebd4bac1b9ed/Untitled.png)]](https://i-blog.csdnimg.cn/blog_migrate/fb4ddddfc067fd16c95b62fddaa19ef5.png)
例如如下一段代码
#include<stdio.h>
int add(int x, int y)
{
return x + y;
}
int caller()
{
int x = 5,y = 10;
int sum = add(x, y);
return sum;
}
int main()
{
caller();
}
下面是它的汇编代码:
caller:
0x401564 push %rbp //保存main函数栈底地址,压栈
0x401565 mov %rsp,%rbp //将main函数的栈顶地址放到%rbp中,栈底移动到%rsp的位置,以便建立新栈帧
0x401568 sub $0x30,%rsp //%rsp中的数据减30,开辟caller函数的栈帧
0x40156c movl $0x5,-0x4(%rbp)//将数据0x5放入相对于栈底-4的地址
0x401573 movl $0xa,-0x8(%rbp)//将数据0xa放入相对于栈底-8的地址
0x40157a mov -0x8(%rbp),%edx//将相对于栈底-8的地址中的数据放入寄存器%edx
0x40157d mov -0x4(%rbp),%eax//将相对于栈底-4的地址中的数据放入寄存器%eax
0x401580 mov %eax,%ecx //将%eax中的数据移动到%ecx
0x401582 call 0x401550 <add>//调用add 函数
0x401587 mov %eax,-0xc(%rbp) //取出返回值放到相对于栈底-0xc的地址中(返回值规定放在eax寄存器中)
0x40158a mov -0xc(%rbp),%eax //将相对于栈底-0xc的地址中的数据放到%eax作为返回值
0x40158d add $0x30,%rsp //栈顶地址加0x30,销毁栈
0x401591 pop %rbp //弹出栈顶的值,放入栈底寄存器中
0x401592 ret //返回
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EZ4C2ZBQ-1668615005745)(%E8%B0%83%E7%94%A8%E5%87%BD%E6%95%B0%E6%97%B6%E6%A0%88%E4%B8%AD%E4%BF%A1%E6%81%AF%E7%9A%84%E5%8F%98%E5%8C%96%EF%BC%88C%E8%AF%AD%E8%A8%80%EF%BC%89%2053aad5c3f5194570ac32ebd4bac1b9ed/Untitled%201.png)]](https://i-blog.csdnimg.cn/blog_migrate/86a5e66f61bd28e4f93309d8e40e6d07.png)
add:
0x401550 push %rbp //将caller的栈底地址压栈
0x401551 mov %rsp,%rbp // 将caller栈顶的地址放入栈底寄存器中,以建立新的栈帧
0x401554 mov %ecx,0x10(%rbp)//将第一个参数放入相对于add 的栈底为0x10地址中
0x401557 mov %edx,0x18(%rbp)//将第二个参数放入相对于add 的栈底为0x18地址中
0x40155a mov 0x10(%rbp),%edx //将第一个参数放到%edx中
0x40155d mov 0x18(%rbp),%eax //将第二个参数放到%eax中
0x401560 add %edx,%eax //将寄存器%dex的数据加上%eax中的数据放到%eax中
0x401562 pop %rbp //弹出栈顶的值,放入栈底寄存器中,销毁栈
0x401563 ret //返回
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LpHtUwAI-1668615005747)(%E8%B0%83%E7%94%A8%E5%87%BD%E6%95%B0%E6%97%B6%E6%A0%88%E4%B8%AD%E4%BF%A1%E6%81%AF%E7%9A%84%E5%8F%98%E5%8C%96%EF%BC%88C%E8%AF%AD%E8%A8%80%EF%BC%89%2053aad5c3f5194570ac32ebd4bac1b9ed/Untitled%202.png)]](https://i-blog.csdnimg.cn/blog_migrate/482c6a409d191e62cfa8b5ae7f7e821d.png)
当add函数运行完之后,栈顶指针ESP会被赋予caller函数的栈顶地址,栈底也会变成caller函数的栈底地址,然后销毁此add函数的栈。
函数的返回值会被规定存在寄存器eax中。
栈底的地址+8存的是调用此函数的命令的下一条命令的地址,执行完add函数之后,会弹出栈顶,以便继续执行下一条命令。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7f03gNPV-1668615005748)(%E8%B0%83%E7%94%A8%E5%87%BD%E6%95%B0%E6%97%B6%E6%A0%88%E4%B8%AD%E4%BF%A1%E6%81%AF%E7%9A%84%E5%8F%98%E5%8C%96%EF%BC%88C%E8%AF%AD%E8%A8%80%EF%BC%89%2053aad5c3f5194570ac32ebd4bac1b9ed/Untitled%203.png)]](https://i-blog.csdnimg.cn/blog_migrate/c9d7274157bdf76c92692836b606945e.png)
本文详细解析了C语言中函数调用时栈帧的变化过程,包括栈帧的建立与销毁、参数传递及返回值处理等内容,并通过具体示例展示了栈帧的工作原理。
3336

被折叠的 条评论
为什么被折叠?



