1、概念
函数调用栈,简称栈。不管是函数的执行还是函数调用,栈都起着非常重要的作用
- 保存函数的局部变量
- 向被调用函数传递参数
- 返回函数的返回值
- 保存函数的返回地址。返回地址是指从被调用函数返回后调用者应该继续执行的指令地址
该文章会帮你很好的了解函数栈——x86_64栈和函数调用
每个函数在执行过程中都需要使用一块栈内存用来保存上述这些值,我们称这块栈内存为某函数的栈帧(stack frame)。当发生函数调用时,因为调用者还没有执行完,其栈内存中保存的数据还有用,所以被调用函数不能覆盖调用者的栈帧,只能把被调用函数的栈帧“push”到栈上,等被调函数执行完成后再把其栈帧从栈上“pop”出去,这样,栈的大小就会随函数调用层级的增加而生长,随函数的返回而缩小。
栈的生长和收缩都是自动的,由编译器插入的代码自动完成(通常可以通过汇编代码看到 push、pop 相关操作),因此位于栈内存中的函数局部变量所使用的内存随函数的调用而分配,随函数的返回而自动释放,所以程序员不管是使用有垃圾回收还是没有垃圾回收的高级编程语言都不需要自己释放局部变量所使用的内存,这一点与堆上分配的内存截然不同。
栈顶和栈底共同决定了一个栈帧。
- 栈底:在未涉及函数调用时,栈底是不变的。通过栈底去访问栈中的变量
- 栈顶:随着入栈、出栈动作,栈顶是动态变化的
栈的生长方向,一定是由 栈底 -> 栈顶。只是说,栈底可能是低地址,也可能高地址。
2、测试程序
用代码判断栈的增长方向,代码转载自文章 用代码判断栈增长方向
/*
* test.c
*/
#include <stdio.h>
#include <stddef.h>
typedef enum {Upward, Downward} Stack_Dir_t;
Stack_Dir_t detect_stack_dir(char * p) {
char local = 0;
if (p) {
return &local > p ? Upward : Downward;
} else {
return detect_stack_dir(&local);
}
}
int main() {
if (detect_stack_dir(NULL) == Upward)
printf("Stack Growth Upward!\n");
else
printf("Stack Growth Downward!\n");
return 0;
}
运行结果:
cfp@cfp-virtual-machine:~/Desktop$ gcc test.c -o test
cfp@cfp-virtual-machine:~/Desktop$ ./test
Stack Growth Downward!
表明,栈的生长方向,为 高地址 -> 低地址,栈底是 高地址。