《深入理解计算机系统(第二版)》CSAPP 第三章 家庭作业

这一章介绍了AT&T的汇编指令 比较重要 本人完成了《深入理解计算机系统(第二版)》(以下简称CSAPP)第三章的家庭作业,并与网上的一些答案进行了对比修正。

感谢博主summerhust的整理,以下贴出AT&T常用汇编指令

AT&T常用汇编指令

数据传送指令

指令 效果 描述
movl S,D D <-- S 传双字
movw S,D D <-- S 传字
movb S,D D <-- S 传字节
movsbl S,D D <-- 符号扩展S 符号位填充(字节->双字)
movzbl S,D D <-- 零扩展S 零填充(字节->双字)
pushl S R[%esp] <-- R[%esp] – 4;M[R[%esp]] <-- S 压栈
popl D D <-- M[R[%esp]];R[%esp] <-- R[%esp] + 4; 出栈

算数和逻辑操作地址:

指令 效果 描述
leal S,D D = &S movl地版,S地址入D,D仅能是寄存器
incl D D++ 加1
decl D D-- 减1
negl D D = -D 取负
notl D D = ~D 取反
addl S,D D = D + S
subl S,D D = D – S
imull S,D D = D*S
xorl S,D D = D ^ S 异或
orl S,D D = D | S
andl S,D D = D & S
sall k,D D = D << k 左移
shll k,D D = D << k 左移(同sall)
sarl k,D D = D >> k 算数右移
shrl k,D D = D >> k 逻辑右移

特殊算术操作:

指令 效果 描述
imull S R[%edx]:R[%eax] = S * R[%eax] 有符号64位乘
mull S R[%edx]:R[%eax] = S * R[%eax] 无符号64位乘
cltd S R[%edx]:R[%eax] = 符号位扩展R[%eax] 转换为4字节
idivl S R[%edx] = R[%edx]:R[%eax] % S;R[%eax] = R[%edx]:R[%eax] / S; 有符号除法,保存余数和商
divl S R[%edx] = R[%edx]:R[%eax] % S;R[%eax] = R[%edx]:R[%eax] / S; 无符号除法,保存余数和商

注:64位数通常存储为,高32位放在edx,低32位放在eax。

条件码:

条件码寄存器描述了最近的算数或逻辑操作的属性。

CF:进位标志,最高位产生了进位,可用于检查无符号数溢出。

OF:溢出标志,二进制补码溢出——正溢出或负溢出。

ZF:零标志,结果为0。

SF:符号标志,操作结果为负。

比较指令:

指令 基于 描述
cmpb S2,S1 S1 – S2 比较字节,差关系
testb S2,S1 S1 & S2 测试字节,与关系
cmpw S2,S1 S1 – S2 比较字,差关系
testw S2,S1 S1 & S2 测试字,与关系
cmpl S2,S1 S1 – S2 比较双字,差关系
testl S2,S1 S1 & S2 测试双字,与关系

访问条件码指令:

指令 同义名 效果 设置条件
sete D setz D = ZF 相等/零
setne D setnz D = ~ZF 不等/非零
sets D D = SF 负数
setns D D = ~SF 非负数
setg D setnle D = ~(SF ^OF) & ZF 大于(有符号>)
setge D setnl D = ~(SF ^OF) 小于等于(有符号>=)
setl D setnge D = SF ^ OF 小于(有符号<)
setle D setng D = (SF ^ OF) | ZF 小于等于(有符号<=)
seta D setnbe D = ~CF & ~ZF 超过(无符号>)
setae D setnb D = ~CF 超过或等于(无符号>=)
setb D setnae D = CF 低于(无符号<)
setbe D setna D = CF | ZF 低于或等于(无符号<=)

跳转指令:

指令 同义名 跳转条件 描述
jmp Label 1 直接跳转
jmp *Operand 1 间接跳转
je Label jz ZF 等于/零
jne Label jnz ~ZF 不等/非零
js Label SF 负数
jnz Label ~SF 非负数
jg Label jnle ~(SF^OF) & ~ZF 大于(有符号>)
jge Label jnl ~(SF ^ OF) 大于等于(有符号>=)
jl Label jnge SF ^ OF 小于(有符号<)
jle Label jng (SF ^ OF) | ZF 小于等于(有符号<=)
ja Label jnbe ~CF & ~ZF 超过(无符号>)
jae Label jnb ~CF 超过或等于(无符号>=)
jb Label jnae CF 低于(无符号<)
jbe Label jna CF | ZF 低于或等于(无符号<=)

转移控制指令:(函数调用):

指令 描述
call Label 过程调用,返回地址入栈,跳转到调用过程起始处,返回地址是call后面那条指令的地址
call *Operand
leave 为返回准备好栈,为ret准备好栈,主要是弹出函数内的栈使用及%ebp

家庭作业参考答案

3.54

x at %ebp+8, y at %ebp+12, z at %ebp+16, return value at %eax

  1. int decode2(int x,int y,int z){
  2. int temp1 = z-y;
  3. int temp2 = temp1<<15;
  4. temp2 = temp2>>15;
  5. return (x^temp1)*temp2;
  6. }

3.55

**ll_t is defined as long long **

  1. void store_prod(ll_t *dest, ll_t x, int y){
  2. *dest = x*y;
  3. }

dest at %ebp+8, x at %ebp+12, y at %ebp+20

  1. movl 12(%ebp),%esi ;get x(long long)的低位
  2. movl 20(%ebp),%eax ;get y(int)
  3. movl %eax,%edx ;备份y
  4. sarl $31,%edx ;获取y的符号位
  5. movl %edx,%ecx ;备份y的符号位
  6. imull %esi,%ecx ;x的低位无符号乘法乘以y的符号位(0或-1 0或全1)
  7. movl 16(%ebp),%ebx ;get x(long long)的高位
  8. imull %eax,%ebx ;x的高位乘以y
  9. addl %ebx,%ecx ;%ecx=x高位*y+x低位*y的符号位(补全下面无符号的缺少部分)
  10. mull %esi ;%edx=(x的低位*y)的高位 %eax=(x的低位*y)的低位(这一步无符号)
  11. leal (%ecx,%edx),%edx;%edx = %ecx+%edx
  12. movl 8(%ebp),%ecx ;get dest
  13. movl %eax,(%ecx) ;dest[0] = (x的低位*y)的低位
  14. movl %edx,4(%ecx) ;dest[1] = (x的低位*y)的高位+x的高位*y+x的低位*y的符号位(0或者-1)
  1. 32位机器模拟64位机器的有符号乘法
  2. x表示 long long x 的值
  3. x = xh*2^32 + xl
  4. x*y = y*xh*2^32 + y*xl 完整形式是96位长 但仅需要64
  5. 因此我们需要y*xl的完整64位和y*xh的低32 并分开存储

3.56

一个知识点:位移操作可以是一个立即数或者是存放在单字节寄存器元素%cl中

  1. A.
  2. x:%esi,n:%ebx,result:%edi,mask:%edx
  3. B.
  4. result=0x55555555; //‭0101 0101 0101 0101 0101 0101 0101 0101‬
  5. mask=0x80000000;
  6. C.
  7. mask!=0
  8. D.
  9. mask每次循环右移n
  10. E.
  11. result每次循环和x&mask的值进行异或
  1. int loop(int x,int n){
  2. int result = 0x55555555;
  3. int mask;
  4. for(mask = 0x80000000;mask!=0;mask=(unsigned)mask>>n){//需要注意逻辑右移 将有符号数转化为无符号
  5. result ^= mask&x;
  6. }
  7. return result;
  8. }

3.57

  1. int cread_alt(int *xp){
  2. int temp = 0;
  3. int *p = xp?xp:&temp;
  4. return *p;
  5. }

3.58

  1. typedef enum {MODE_A, MODE_B, MODE_C, MODE_D, MODE_E} mode_t;
  2. int switch3(int *p1, int *p2, mode_t action){
  3. int result = 0;
  4. switch(action){
  5. case MODE_A:
  6. result = *p1;
  7. *p1 = *p2;
  8. break;
  9. case MODE_B:
  10. result = *p1+*p2;
  11. *p2 = result;
  12. break;
  13. case MODE_C:
  14. *p2 = 15;
  15. result = *p1;
  16. case MODE_D:
  17. *p2 = *p1;
  18. case MODE_E:
  19. result = 17;
  20. break;
  21. default:
  22. result = -1;
  23. break;
  24. }
  25. return result;
  26. }

选一个部分的汇编代码注释解释一下

  1. .L14(MODE_B):
  2. movl 12(%ebp),%edx ;get p2 reuslt = p2
  3. movl (%edx),%eax ;temp_p2 = p2
  4. movl %eax,%edx ;result = *p2
  5. movl 8(%ebp),%ecx ;get p1 temp_p1 = p1
  6. addl (%ecx),%edx ;result += *p1此时result = *p1+*p2
  7. movl 12(%ebp),%eax ;get p2
  8. movl %edx,(%eax) ;*p2 = result
  9. jmp .L19 ;

3.59

  1. int swith_prob(int x, int n){
  2. int result = x;
  3. switch(n){
  4. case 40:
  5. case 42:
  6. result <<= 3;
  7. break;
  8. case 43:
  9. result >>= 3;
  10. break;
  11. case 44:
  12. result <<= 3;
  13. result -= x;
  14. case 45:
  15. result *= result;
  16. case 41:
  17. default:
  18. retult += 17;
  19. break;
  20. }
  21. return result;
  22. }

解释和部分汇编代码注释

  1. gdb打印出来的内容可以看出40的情况和42是一样的因此40内容为空,其后紧跟42
  2. 44跳转至后紧接着执行了45跳转的内容 45之后也无跳转 因此4445没有break
  3. 41的情况与default相同因此41置为空写在defaul
  4. mov 0xc(%ebp),%eax ;%ebp+12的位置获取第二个参数,即n
  5. sub 0x28,%eax ;n-40(0x2816进制 转为10进制为2*16+8=40)
  6. cmp 0x5,%eax ;n-40 5
  7. ja 8048435(switch_pro+0x15);n-40-5>0? n若大于45直接返回

3.60

  1. A.从汇编代码看出
  2. A[i][j][k] = A+(63i+9j+k)*4
  3. B.
  4. 解决方程T=9, S*T=63, R*S*T*4=2772
  5. T = 9
  6. S = 7
  7. R = 11

3.61

  1. int var_prod_ele(int n, int A[n][n], int B[n][n], int i,int k){
  2. int result = 0;
  3. int *a_l = &A[i][0];
  4. int *a_r = &A[i][n];
  5. int *b_l = &b[0][k];
  6. while(a_l!=a_r){
  7. result += (*a_l)*(*b_l);
  8. b_l += n;
  9. a_l++;
  10. }
  11. return result;
  12. }

可自行查看汇编代码验证

  1. L3:
  2. movl (%ebx), %ecx
  3. imull (%edx), %ecx
  4. addl %ecx, %eax
  5. addl %edi, %ebx
  6. addl $4, %edx
  7. cmpl %edx, %esi
  8. jne L3

3.62

  1. A.
  2. M = 76/4 = 19
  3. B.
  4. %edi 保存 i
  5. %ecx 保存 j

使用指针进行优化

  1. void transpose(int A[M][M]){
  2. int i,j;
  3. for(i=0;i<M;i++){
  4. int *temp1 = &A[i][0];
  5. int *temp2 = &A[0][i];
  6. for(j=0;j<i;j++){
  7. int t = *temp1;
  8. *temp1 = *temp2;
  9. *temp2 = t;
  10. temp1++;
  11. temp2 += M;
  12. }
  13. }
  14. }

3.63

  1. #define E1(n) 3*n
  2. #define E2(n) 2*n-1

3.64

  1. A.
  2. 8(%ebp) result
  3. 12(%ebp) s1.p
  4. 16(%ebp) s1.v
  5. B.
  6. ------------%ebp
  7. s2.sum
  8. s2.prod
  9. s1.v
  10. s1.p
  11. &s2
  12. ------------%esp
  13. C.
  14. 将结构体变量的各个成员的值传入函数
  15. D.
  16. 将返回变量的地址传递出去

3.65

  1. A=3,B=7

3.66

这个题看了好久,不得不佩服GCC

写下详细注释

  1. push %ebp
  2. mov %esp,%ebp
  3. push %ebx
  4. mov 0x8(%ebp),%eax ;get i
  5. mov 0xc(%ebp),%ecx ;get *bp
  6. imul $0x1c,%eax,%ebx ;28*i
  7. lea 0x0(,%eax,8),%edx;8*i
  8. sub %eax,%edx ;7*i
  9. add 0x4(%ecx,%ebx,1),%edx ;7i+(bp+28i+4)注意bp+4a_struct a的首地址 +28i即是bp->a[i]即一个a_struct大小是28 同时 有*ap = (bp+28i+4)
  10. mov 0xc8(%ecx),%eax ;bp->right = bp+200
  11. add (%ecx),%eax ;bp->left + bp->right 28*CNT = 200 - 4 CNT = 7
  12. mov %eax,0x8(%ecx,%edx,4);bp+8+4*(7i+(bp+28i+4))=(bp+28*i+4+4)(即ap->idx+4apx->x[0])+*(bp+0x1c*i+4)*4
  13. pop %ebx
  14. pop %ebp
  15. ret
  1. A.
  2. CNT = 7
  3. B.
  4. a_struct{
  5. int idx;
  6. int x[6];
  7. }

其实分析出大小就能做这个题 并不需要完全看懂汇编代码

3.67

  1. A.
  2. e1.p:0
  3. e1.x:4
  4. e2.y:0
  5. e2.next:4
  6. B.8
  7. C.
  8. void proc(union ele *up)
  9. {
  10. up->e2.next->e1.x=*(up->e2.next->e1.p) - up->e2.y;
  11. }

3.68

  1. void good_echo()
  2. {
  3. char c;
  4. int x = 0;
  5. while( x=getchar(), x!='\n' && x!=EOF)
  6. {
  7. putchar(x);
  8. }
  9. }

3.69

  1. long trace(tree_ptr tp){
  2. long result = 0;
  3. while(tp){
  4. result = tp->val;
  5. tp = tp->left;
  6. }
  7. return result;
  8. }

输出二叉树最左边节点的值

3.70

  1. long traverse(tree_ptr tp){
  2. if(!tp) return 9223372036854775807;
  3. long v = tp->val;
  4. long left = traverse(tp->left);
  5. long result = traverse(tp->right);
  6. if(left <= result) result = left;
  7. if(v <= result) result = v;
  8. return result;
  9. }

或者换一种写法

  1. long traverse(tree_ptr tp){
  2. long result = 9223372036854775807;
  3. if(tp){
  4. long lv = traverse(tp->left);
  5. long rv = traverse(tp->right);
  6. result = lv <= rv ? lv : rv;
  7. result = result > tp->val ? tp->val : result;
  8. }
  9. return result;
  10. }

求二叉树节点最小值

指令 效果
cmovle s,r 小于或等于 s->r
cmovg s,r 大于 s->r

详见数据传送指令

参考文献

《深入理解计算机系统(第二版)》

https://blog.csdn.net/summerhust/article/details/7404340

https://blog.csdn.net/maidou0921/article/details/53907971

CSAPP深入理解计算机系统(第二版)第三章家庭作业答案的更多相关文章

  1. C++第三章课后作业答案及解析---指针的使用

    今天继续完成上周没有完成的习题---C++第三章课后作业,本章题涉及指针的使用,有指向对象的指针做函数参数,对象的引用以及友元类的使用方法等 它们具体的使用方法在下面的题目中会有具体的解析(解析标注在 ...

  2. 深入理解计算机系统第二版习题解答CSAPP 2.11

    在2.10中的inplace_swap函数的基础上,你决定写一段代码,实现将一个数组中的元素两端依次对调,你写出下面这个函数: void reverse_array(int a[], int cnt) ...

  3. 深入理解计算机系统第二版习题解答CSAPP 2.10

    对于任一位向量a,有a ^ a = 0.考虑下面的程序: void inplace_swap(int *x, int *y) { *y = *x ^ *y; *x = *x ^ *y; *y = *x ...

  4. 深入理解计算机系统第二版习题解答CSAPP 2.20

    T2Uw(w)=x, x≥0时 T2Uw(w)=x+2w, x<0时 利用上面的公式,重新计算2.19的问题.

  5. 深入理解计算机系统第二版习题解答CSAPP 2.19

    在2.17的基础上完成下表: x 十六进制 T2U(x) -8 0x8 -3 0xD -2 0xE -1 0xF 0 0x0 5 0x5

  6. 深入理解计算机系统第二版习题解答CSAPP 2.18

    将32位补码表示的数转换为10进制数. 32位补码 十进制 0x1b8 0x14 0xFFFFFE58 -424 0xFFFFFE74 -396 0x44 0xFFFFFEC8 -312 0x10 0 ...

  7. 深入理解计算机系统第二版习题解答CSAPP 2.17

    假设w=4,我们能给每个可能的十六进制数字赋予一个数值,假设用一个无符号或者补码表示.完成下表: x 无符号(B2U(x)) 补码(B2T(x)) 十六进制 二进制 0xE 1110 14 -2 0x ...

  8. 深入理解计算机系统第二版习题解答CSAPP 2.16

    填写下表,说明不同移位运算对单字节数的影响. x x<<3 x>>2(逻辑) x>>2(算术) 十六进制 二进制 二进制 十六进制 二进制 十六进制 二进制 十六进 ...

  9. 深入理解计算机系统第二版习题解答CSAPP 2.15

    只使用位级运算和逻辑运算,编写一个C表达式,它等价于x==y.换句话说,当x和y相等时它将返回1,否则就返回0. !(x ^ y)

随机推荐

  1. Codeforces #381(div2)

    A.题目:http://codeforces.com/contest/740/problem/A 题意:现有n本书,买一本书需要花a元,两本书b元,三本书c元,问买够书是4的倍数所需要的最小花费 思路 ...

  2. Memory Layout for Multiple and Virtual Inheritance

    Memory Layout for Multiple and Virtual Inheritance(By Edsko de Vries, January 2006)Warning. This art ...

  3. iView页面Modal中内嵌Tabs,重新显示Modal时默认选中Tabs的第一项

    文档中说激活面板的name用value,页面第一次加载的时候可以,放在modal里就不好使了,每次打开的时候总显示上一次离开时的界面. 真正能用的是 this.$refs.tabs.activeKey ...

  4. Spring mvc 整合PageHelper

    Integer page=queryBean.getPage(); Integer pageSize=queryBean.getPageSize(); response.setContentType( ...

  5. Recurrent Neural Networks(RNN) 循环神经网络初探

    1. 针对机器学习/深度神经网络“记忆能力”的讨论 0x1:数据规律的本质是能代表此类数据的通用模式 - 数据挖掘的本质是在进行模式提取 数据的本质是存储信息的介质,而模式(pattern)是信息的一 ...

  6. 金融量化分析【day111】:Pandas-分组与聚合

    一.分组与聚合 在数据分析中,我们有时需要将数据拆分,在每一个特定的组里进行运算 1.实验数据准备 a = pd.read_csv('601318.csv') a 数据如下: 实验数据 2.示例 df ...

  7. 语义化标签和jQuery选择器

    关于语义化标签 https://blog.csdn.net/nongweiyilady/article/details/53885433 更详细的语义化标签:https://www.cnblogs.c ...

  8. 第五节:WebApi的三大过滤器

    一. 基本说明  1. 简介: WebApi下的过滤器和MVC下的过滤器有一些区别,首先我们要注意的是通常建WebApi项目时,会自动把MVC的程序集也引入进来,所以我们在使用WebApi下的过滤器的 ...

  9. oldboy s21day10

    #!/usr/bin/env python # -*- coding:utf-8 -*-   # 1.写函数,函数可以支持接收任意数字(位置传参)并将所有数据相加并返回. ''' def func(* ...

  10. Spring Security 之基本概念

    Spring Security 是一个安全框架, 可以简单地认为 Spring Security 是放在用户和 Spring 应用之间的一个安全屏障, 每一个 web 请求都先要经过 Spring S ...