深入理解计算机系统 csapp 家庭作业(第三章完整版)


第三章

3.58

#include <stdio.h>
long decode2(long x,long y,long z){
    long ans=y-z;
    return (ans*x)^(ans<<63>>63);
}

int main(){
    printf("%d\n",decode2(100,2,2));
}

3.59

原理:
在这里插入图片描述
可以看出2128位溢出,因此只需计算后两项即可
即:在这里插入图片描述

store_prod:
  movq %rdx, %rax   		  //将y保存在%rax中
  cqto						  //对y进行符号位扩展,rdx=(-1)y_63
  movq %rsi, %rcx 			  //将x保存在%rcx中   
  sarq $63, %rcx			  //将x算数右移63位,得到x符号位,%rcx=(-1)x_63
  imulq %rax, %rcx     	 	  //用y乘x的符号位,得x63y
  imulq %rsi, %rdx   		  //用x乘y的符号位,得y63x 
  addq %rdx, %rcx             //将x63y+y63x保存在%rcx中   
  mulq %rsi                   //无符号数乘法uxuy,此时%rdx保存结果的高64位,%rax保存结果的低64位
  addq %rcx, %rdx			  //将x63y+y63x加到uxuy的高64位上
  movq %rax, (%rdi)   		  //保存结果的低64位
  movq %rdx, 8(%rdi)  		  //保存结果的高64位
  ret

3.60

A.x:%rdi,	n:%esi,	  result:%rax, 	 mask:%rdx
B.result=0;	mask=1;
C.mask!=0;
D.mask=mask<<n;
E.result|=(x&mask);
F.//补全代码
#include <stdio.h>
long loop(long x,int n){
    long result=0;
    long mask;
    for(mask=1;mask!=0;mask=mask<<n;){
        result|=(x&mask);
    }
    return result;
}

3.61

#include <stdio.h>
long cread_alt(long *xp) {
  return (!xp ? 0 : *xp);
}

3.62

typedef enum {MODE_A, MODE_B, MODE_C, MODE_D, MODE_E} mode_t;

long switch3(long *p1, long *p2, mode_t action){
    long result=0;
    switch (action){
       case MODE_A:
        result=*p2;
        *p2=*p1;
        break;
       case MODE_B:
        *p1=*p1+*p2;
        result=*p1;
        break;
       case MODE_C:
        *p1=59;
        result=*p2;
        break;
       case MODE_D:
        *p1=*p2;
        result=27;
        break;
       case MODE_E:
        result=27;
        break;
       default:
        result=12;
        break;
    }
    return result;
}

3.63

long switch_prob(long x, long n){
    long result=x;
    switch (n){
        case 60:
        case 62:
        result=8*x;
        break;
        case 63:
        result=x>>3;
        break;
        case 64:
        result=(x<<4)-x;
        case 65:
        result=x*x;
        default:
        result=x+0x4B;
    }
    return result;
}

3.64

store_ele:
  leaq (%rsi, %rsi, 2), %rax    // t1 = j*3
  leaq (%rsi, %rax, 4), %rax    // t1 = j*13
  movq %rdi, %rsi               // t2 = i
  salq $6, %rsi                 // t2 = i*64
  addq %rsi, %rdi               // t3 = i*65
  addq %rax, %rdi               // t3 = i*65 + j*13
  addq %rdi, %rdx               // t4 = i*65 + j*13 + k
  movq A(,%rdx,8), %rax         // t1 = *(A + 8*t4)
  movq %rax, (%rcx)             // *dest = t1
  movl $3640, %eax              // return 3640
  ret

A. &A[i][j][k] = A+ L (S* T * i + T * j + k)
B. 由题目中可知
S * T = 65 ;
T = 13 ;
8 * S * T * R = 3640
解得R = 7 ; S = 5 ; T = 13

3.65

A. %rdx(由第六行可知,每次j加一个字节,因此%rdx为A[i][j]
B. %rax
C.M=120/8=15

3.66

sum_col:
  leaq 1(,%rdi,4), %r8        // t1 = n*4 + 1
  leaq (%rdi,%rdi,2), %rax    // t2 = n*3
  movq %rax, %rdi             // t3 = n*3
  testq %rax, %rax            // test n*3
  jle .L4                     // n*3 <= 0, jump .L4
  salq $3, %r8                // t1 = t1*8 = 8*(n*4 + 1)
  leaq (%rsi,%rdx,8), %rcx    // t4 = j*8 + A
  movl $0, %eax               // t2 = 0
  movl $0, %edx               // t5 = 0
.L3:
  addq (%rcx), %rax           // t2 = *(t4) = *(A + j*8)
  addq $1, %rdx               // t5 = t5+1
  addq %r8, %rcx              // t4 = t1+t4 = A + j*8 + 8*(n*4 + 1)
  cmpq %rdi, %rdx             // cmp t5 & t3
  jne .L3                     // if t5 != n*3, loop
  rep
  ret
.L4:
  movl $0, %eax               // return 0
  ret

NR(n) == n3; NC(n) == n4 + 1

由第十六行jne跳转指令可知,
只有当%rdx等于3时才跳出循坏,因此,NR(n) == n*3;

由第十四行addq指令可知,%r8每次加8*(4n+1),所以每行由4n+1个数,因此,NC(n) == n*4 + 1。

3.67

A.

相对于%rsp的偏移量储存再栈上的值
%rsp+64返回地址
%rsp+24z
%rsp+16s.p=&z
%rsp+8y
%rspx

B.传递的一个新的地址值%rsp+64给process。
C.通过%rsp+偏移量访问。
D.process从%rsp+64开始存储地址,最终回到这个地址,通过&rdi+偏移量来寻址各个元素。
E.调用函数后,%rsp会减8存储返回地址,即此处%rsp+24=调用前%rsp+16

相对于%rsp的偏移量储存再栈上的值
%rsp+80z
%rsp+72x
%rsp+64y
%rsp+24z
%rsp+16s.p=&z
%rsp+8y
%rspx

F.像结构体这种无法用一个寄存器存储的参数都是用内存传参,传入时,通过%rsp+偏移量将元素存储在栈上,返回时,通过%rdi+偏移量寻址,将元素返回。

3.68

依据汇编代码及数据对齐规则:

q->t = 8(%rsi),即str2的t从第8位开始,按照8位对齐sizeof(array[B])小于等于8,且下边的t是int类型占4个字节,所以sizeof(array[B])大于4,可以得到4<B<=8。

q->u = 32(%rsi),即str2的u从第32位开始,则s占20个字节,得sizeof(s[A])<=20,且下边的u是long类型占8个字节,所以sizeof(s[A])大于20-8=12,可以得到6<A<=10。

p->y = 184(%rdi),即str1的y从第184位开始,所以184-8<AB4<=184,可以得到44<A*B<=46。

由以上三个约束条件,解得A = 9 B = 5

3.69

A.
由代码第一二行可知,first+a一共占了0x120个字节;0X120的十进制表示为288
由第三四五行可知,&bp->a[i]的地址计算公式为40i+&bp+8;
故int对齐之后占8个字节(而不是int类型4字节),a[i]大小为40;
即CNT=(288-8)/40=7;
B.

typedef struct{
		long idx;
		long x[4];
}

由代码第七八行可知ap->x[ap->idx]的地址计算公式为&bp+16+idx*8;
+16说明first和idx共占16个字节,idx是a[i]第一个元素,占8个字节;
movslq将int转成long,说明x是long类型;
则数组x的个数为(40-8)/8=4

3.70

A.
e1.p: 0
e1.y: 8
e2.x: 0
e2.next: 8

B. 16

C. up->e2.x = *(up->e2.next->e1.p) - up->e2.next->e1.y

 mov 8(%rdi), %rax 	 // rax = up->e2.next
 mov (%rax), %rdx 	 // rdx = up->e2.next->e1.p
 mov (%rdx), %rdx  	 // rdx = *(up->e2.next->e1.p)
 subq 8(%rax), %rdx  // rdx -= up->e2.next->e1.y
 mov %rdx, (%rdi) 
 // up->e2.x = *(up->e2.next->e1.p) - up->e2.next->e1.y

代码第二行,偏置为8,且数据为指针,所以只能是up->e2.next
第三行偏置为0,且也为指针,所以为up->e2.next->e1.p
第四行,没有偏置,仍然为指针,为 (up->e2.next->e1.p)
于是,减数为
(up->e2.next->e1.p)

第五行,%rax中保存up->e2.next,故8(%rax)为up->e2.next->e1.y
于是,被减数为up->e2.next->e1.y

第六行,结果赋值给e2.x
得等式左侧为up->e2.x

3.71

fgets函数用来从文件中读入字符串。
fgets函数的调用形式如下:fgets(str,n,fp);
此处,fp是文件指针;str是存放在字符串的起始地址;n是一个int类型变量。

#include <stdio.h>

void good_echo(void){
    const int SIZE = 100;
    char buf[SIZE];
    while(1){
        char* p=fgets(buf,SIZE,stdin);
        if(p==NULL){
            break;
        }
        printf("%s",p);
    }
    return;
}
int main(){
    good_echo();
    return 0;
}

3.72

A.s2=s1−[(n∗8+30)&0xFFFFFF0],舍入到最近的16的倍数
若为奇数,则s2=s1−(n∗8+24)
若为偶数,则s2=s1−(n∗8+16)

B.p=(s2+15)&0XFFFFFFF0
确保了p数组的起始地址为16的整数倍
C.

最大最小
e1=1(此时e2最大等于15)e1=24(此时e2最小等于0)
n为偶数n为奇数
n%16=1n%16=0

D.
s2保证了能容纳下8*n字节的最小的16的倍数
p保证了自身对齐16

3.73

#include <stdio.h>
#include <assert.h>

typedef enum {NEG, ZERO, POS, OTHER} range_t;

range_t find_range(float x) {
  __asm__(
      "vxorps %xmm1, %xmm1, %xmm1\n\t"
      "vucomiss %xmm1, %xmm0\n\t"
      "jp .P\n\t"
      "ja .A\n\t"
      "jb .B\n\t"
      "je .E\n\t"
      ".A:\n\t"
      "movl $2, %eax\n\t"
      "jmp .Done\n\t"
      ".B:\n\t"
      "movl $0, %eax\n\t"
      "jmp .Done\n\t"
      ".E:\n\t"
      "movl $1, %eax\n\t"
      "jmp .Done\n\t"
      ".P:\n\t"
      "movl $3, %eax\n\t"
      ".Done:\n\t"
      );
}

int main(int argc, char* argv[]) {
  range_t n = NEG, z = ZERO, p = POS, o = OTHER;
  assert(o == find_range(0.0/0.0));
  assert(n == find_range(-2.3));
  assert(z == find_range(0.0));
  assert(p == find_range(3.33));
  return 0;
}

3.74

#include <stdio.h>
#include <assert.h>

typedef enum {NEG, ZERO, POS, OTHER} range_t;

range_t find_range(float x) {
  __asm__(
      "vxorps %xmm1, %xmm1, %xmm1\n\t"
      "movq $1, %rax\n\t"
      "movq $2, %r8\n\t"
      "movq $0, %r9\n\t"
      "movq $3, %r10\n\t"
      "vucomiss %xmm1, %xmm0\n\t"
      "cmovaq %r8, %rax\n\t"
      "cmovbq %r9, %rax\n\t"
      "cmovpq %r10, %rax\n\t"
      );
}

int main(int argc, char* argv[]) {
  range_t n = NEG, z = ZERO, p = POS, o = OTHER;
  assert(o == find_range(0.0/0.0));
  assert(n == find_range(-2.3));
  assert(z == find_range(0.0));
  assert(p == find_range(3.33));
  return 0;
}

3.75

A

n实部虚部
1%xmm0%xmm1
2%xmm2%xmm3
3%xmm4%xmm5
n

B
%xmm0是实部,%xmm1是虚部

  • 14
    点赞
  • 136
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
### 回答1: 深入理解计算机系统(CSAPP)是由Randal E. Bryant和David R. O'Hallaron编写的经典计算机科学教材。该教材通过涵盖计算机体系结构、机器级别表示和程序执行的概念,帮助学生深入理解计算机系统的底层工作原理和运行机制。 深入理解计算机系统的练习题对于学生巩固并应用所学知识非常有帮助。这些练习题涵盖了计算机硬件、操作系统和编译器等多个领域,旨在培养学生解决实际问题和设计高性能软件的能力。 对于深入理解计算机系统的练习题,关键是通过实践进行学习。在解答练习题时,应根据课本提供的相关知识和工具,仔细阅读问题描述,并根据实际需求设计相应的解决方案。 在解答练习题时,需要多角度思考问题。首先,应准确理解题目要求,并设计合适的算法或代码来解决问题。其次,应考虑代码的正确性和效率,以及对系统性能的影响。此外,还要注意处理一些特殊情况和异常情况,避免出现潜在的错误或安全漏洞。 解答练习题的过程中,应注重查阅相关资料和参考优秀的解答。这可以帮助我们扩展对问题的理解,并学习他人的思路和解决方法。同时,还可以通过与同学和老师的讨论,共同探讨问题和学习经验。 总之,通过解答深入理解计算机系统的练习题,可以帮助学生巩固所学知识,同时培养解决实际问题和设计高性能软件的能力。这是一个学以致用的过程,可以加深对计算机系统运行机制和底层工作原理的理解。 ### 回答2: 理解计算机系统(CSAPP)是一本经典的计算机科学教材,通过深入研究计算机系统的各个方面,包括硬件、操作系统和编程环境,对于提高计算机科学专业知识与能力具有很大帮助。 练习题是CSAPP中的重要部分,通过练习题的完成,可以加深对计算机系统的理解,并将理论知识转化为实践能力。练习题的数量、难度逐渐递增,从简单的概念与基础问题到复杂的系统设计与实现。 在解答练习题时,首先需要对题目进行仔细阅读和理解,明确题目的要求和限制条件。然后,可以利用课堂讲解、教材内容、网络资源等进行查阅和学习相应的知识。同时,还可以参考课后习题解答等资料,了解一些常见的解题方法和思路。 在解答练习题时,可以利用计算机系统的工具和环境进行实际测试和验证。例如,可以使用调试器、编译器和模拟器等工具对程序或系统进行分析和测试。这样可以更加深入地理解问题的本质,并找到恰当的解决方法。 另外,解答练习题时还可以与同学、教师和网上社区进行交流和讨论。这样可以互相学习和交流解题思路,共同解决问题。还可以了解不同的解题方法和技巧,提高解题效率和质量。 练习题的解答过程可能会遇到一些困难和挑战,例如理论知识的不足、复杂问题的分析与解决。但是通过不断地思考和实践,相信可以逐渐提高解题能力,更好地理解计算机系统。 总之,深入理解计算机系统(CSAPP)练习题是提高计算机科学专业知识和能力的重要途径。通过仔细阅读和理解题目,查阅相关知识,利用计算机系统工具和环境进行实践,与他人进行交流和讨论,相信可以更好地理解计算机系统的各个方面,并将知识转化为实际能力。 ### 回答3: 《深入理解计算机系统(CSAPP)》是计算机科学领域的经典教材之一,对于深入理解计算机系统的原理、设计和实现起到了极大的帮助。在阅读这本书的过程中,书中的习题也是非常重要的一部分,通过做习题,我们可以更好地理解书中所讲的概念和思想。 CSAPP的习题涵盖了课本中各个章节的内容,从基础的数据表示和处理、程序的机器级表示、优化技术、程序的并发与并行等方面进行了深入探讨。通过解答习题,我们可以对这些知识进行实践应用,巩固自己的理解,并培养自己的解决问题的思维方式。 在解答习题时,我们需要充分理解题目要求和条件,并从知识的角度进行分析。有些习题可能需要进行一些编程实践,我们可以通过编程实现来验证和测试我们的思路和解决方案。在解答问题时,我们还可以查阅一些参考资料和网上资源,充分利用互联网的学习资源。 在解答习题时,我们需要保持积极的思维和态度。可能会遇到一些困难和挑战,但是通过坚持和努力,我们可以克服这些困难,提高我们的解决问题的能力。同时,我们还可以通过与同学或者其他人进行讨论,相互分享解题经验和思路,从而更好地理解问题。 综上所述,通过深入理解计算机系统(CSAPP)的习题,我们可以进一步巩固和深化对计算机系统的理解。掌握这些知识,不仅可以提高我们在计算机领域的能力,还可以为我们未来的学习和职业发展奠定重要的基础。因此,认真对待CSAPP的习题,是我们在学习计算机系统知识中不可或缺的一部分。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值