深入理解计算机系统 学习笔记(2)
- 函数调用实例
int swap_add(int* xp,int* yp);
int caller()
{
int arg1 = 534;
int arg2 = 1057;
int sum = swap_add(&arg1 , &arg2);
int diff = arg1 - arg2;
retur sum * diff;
}
int swap_add(int* xp,int* yp)
{
int x = * xp;
int y = * yp;
*xp = y;
*yp = x;
return x + y;
}
(蓝色箭头是“指向”,红色箭头是“偏移量”,绿色箭头是解释说明)
-
arg1和arg2必须存放在栈中,因为我们必须为它们生成地址。swap_add中的变量int x和int y可以存放在寄存器中。
-
分配在栈上的24个字节,8个用于局部变量,8个用于参数,8个未使用,这是因为GCC认识所有的栈空间都应该是16的整数倍。这样保证数据放的严格对齐。
-
经过调用swap_add之后栈的信息又恢复到最初的状态。
-
许多函数编译后不需要栈帧。如果所有的局部变量都能保存在寄存器中,而且这个函数又不会调用其他函数(叶子过程),那么需要栈的唯一原因就是用来保存返回值。特别是dui’yu所以,虽然C语言中有寄存器变量,但是如果这个函数的变量很少的话,及时不标明这个变量是寄存器,它也会被加载到寄存器中去。
-
函数需要栈帧的原因有如下几个:
-
局部变量太多,不能都放在寄存器中。
-
有些局部变量是数组或者结构。
-
函数用&来计算一个局部变量的地址。
-
函数必须将栈上的某些参数传递给另外一个函数.
-
在修改一个被调用着保存寄存器之前,函数需要保存其他状态。
- 栈破坏检测和栈保护:在C语言中,没有可靠的方法来防止对数组的越界写操作。数组越界,是栈溢出后发现这个错误然后抛出。
echo是一个函数,存放了char buf[8]的一个局部变量。其思想为:
在栈帧中任何局部缓冲区与栈状态之间存储一个特殊的金丝雀(canary)值,也成为哨兵值(guard value)是在程序每次运行时随机产生的。因此如果这个哨兵值改变了说明栈溢出了。
- 栈随机化
比如,多次运行下面的代码,本地变量的地址是不变的.
int main()
{
int local;
printf("local at %p\n",&local);
return 0;
}