这个主要写一点关于在C里面,堆栈是怎么保存数据的,以及调用函数时,堆栈指针的变化。
很多人都知道,在程序里面,局部变量是在栈里面保存的,局部变量是有作用域的,这些,其实都是通过栈来实现的。
下面,通过一些简单的程序,来看看程序的栈,到底是怎么一回事,栈是怎么实现保存函数调用前后的信息的,为什么递归容易爆栈的问题。
首先说明两个寄存器
1.rbp:栈帧指针,具体应该是指向当前函数栈的栈底,是不动的。实际的作用应该就是类似于一个基址,通过这个基址上栈中变量的寻址。
2.rsp:栈顶指针。
3.rip:指令寄存器。存储cpu读取指令的地址
首先,写了一个比较简单的C程序:
#include <stdio.h>
#include <stdlib.h>
int add(int a, int b)
{
int c=a+b;
return c;
}
int main()
{
int a=0x1;
int b=0x10;
int c=0x100;
int sum=add(a,b);
sum=add(sum,c);
return 0;
}
经过反汇编之后,得到的main的汇编程序如下:
push %ebp
%esp,%ebp
sub $0x18,%esp
movl $0x1,-0x10(%ebp)
movl $0x10,-0xc(%ebp)
movl $0x100,-0x8(%ebp)
mov -0xc(%ebp),%eax
mov %eax,0x4(%esp)
mov -0x10(%ebp),%eax
mov %eax,(%esp)
call 80483b4 <add>
mov %eax,-0x4(%ebp)
mov -0x8(%ebp),%eax
mov %eax,0x4(%esp)
mov -0x4(%ebp),%eax
mov %eax,(%esp)
call 80483b4 <add>
mov %eax,-0x4(%ebp)
mov $0x0,%eax
leave
ret
通过这个,可以画出main函数的栈:
通过看main的栈的结构,有一点需要注意,而传入函数add的参数a和b,都在main栈的最后两个位置里面。在mian函数里面调用的函数,其参数的拷贝是在main函数的栈里面的,而不是拷贝在add函数的栈里面。
所以函数add(a,b),通过在main的栈里面添加一个a,b的副本,来传递给add函数,所以传递的是参数的一个拷贝,而不是直接把参数传递进去。
接下里看add的汇编程序:
080483b4 <add>:
push %ebp
mov %esp,%ebp
sub $0x10,%esp
mov 0xc(%ebp),%eax
mov 0x8(%ebp),%edx
add %edx,%eax
mov %eax,-0x4(%ebp)
mov -0x4(%ebp),%eax
leave
ret
通过汇编,可以得到程序第一次调用add的栈信息:
<