《深入理解计算机系统》 练习题3.27-3.28

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/anlian523/article/details/84501336

3.27

在这里插入图片描述
要求你将书中的阶乘函数,利用guarded-do的翻译策略,转换成c的goto版本,答案如上图。
注意第一次测试为if(n <= 1),这是因为,第一次测试实际是2 <= n,它的反面是2 > nn < 2n <= 1

3.28 反转x的二进制

在这里插入图片描述
题面上的汇编如上,在用汇编推理c语句时,对于for循环的测试条件,我刚开始没有想通,因为没有看见cmp指令。
原理:jne .L10是检测ZF标志位的,与它最近的语句subq $1, %rdx是有可能置ZF为1的,当%rdx为0时。

long fun_b(unsigned long x){
	long val = 0;
	long i;
	for(i = 64; i != 0; i--){
		val = (val << 1) | (x & 0x1);
		x >>= 1;
	}
	return val;
}

此函数用来获得x的二进制的反转。
x & 0x1获得当前x的第0位的数。val << 1将已经存储的二进制数移到更高一位上去。x >> 1既然当前x的第0位已经存储在了val,那么便丢弃掉第0位,右移1位,把第1位的数作为新的第0位的数。

3.7.5 寄存器中的局部存储空间

除了栈指针%rsp外,寄存器分为被调用者保存寄存器,和调用者保存寄存器。

在过程P调用Q时,如果值放在了被调用者保存寄存器中,那么它们的值在Q返回到P时,与P刚调用Q时,是一样的。Q如何保证这些值不变(要么不去碰它,要么暂时先放入栈帧中),对于P来说不用知道,因为对于P来说是透明的,P只需要在调用Q之前,将需要存储的值放入这些被调用者保存寄存器即可。
在这里插入图片描述
对于如上汇编代码,有三点需要注意:
1.两次push和两次pop。pushq %rbppushq %rbx先将寄存器中的值放入栈中,注意两个都是被调用者保存寄存器。也许你会觉得奇怪,第5和第8行已经按照要求,在调用Q之前把需要存储的值存入到了%rbp %rbx,为什么还要把%rbp %rbx的刚开始的值入栈存起来呢。

这是因为,刚开始的时候%rbp %rbx这两个寄存器可能就已经存了需要保存的值,但因为调用Q所以%rbp %rbx要暂时另作它用。换个角度说,当前的调用者P可能是别的过程的被调用者。这也就是原文中的“当然,要先把之前的值保存到栈上”这句话的意思。

2.为了存调用返回地址,需要手动分配栈空间。subq $8, %rsp这里分配的8字节栈空间,存了两次调用返回地址(分别是第8行和第11行的指令的地址),这里的8字节栈空间是被第二次利用了。

3.注意pushq %rbppushq %rbxsubq $8, %rsp的顺序,它们与后面第12、13、14行指令的顺序,这里是符合栈的先进后出的。

栈指针%rsp

阅读了这么多汇编代码后,总结下栈指针%rsp。
1.栈指针%rsp它其实不算是个寄存器,顾名思义,它是一个指针,指向当前栈顶地址。

2.比如%rax,它一个普通的寄存器,取%rax时,就会取它装有的8字节数据,可能它是个指针;取(%rax)时,则成了内存引用,会取出这个指针指向的数据;
%rsp,它是栈指针,取%rsp时,返回的是当前栈指针的地址形如0x7fffffffe820;取(%rsp)时,则可能取出从820-827这8个字节里面存的数据;而一般来说,我们不需要取%rsp,因为要栈顶地址来根本没用啊。

猜你喜欢

转载自blog.csdn.net/anlian523/article/details/84501336