深入理解计算机系统(第三版) 第三章 家庭作业参考答案 CSAPP HOMEWORK SOLUTIONS (持续更新中)

人非圣贤孰能无过,欢迎大家提问与纠错


3.58

long decode2(long x, long y, long z) {
    y -= z;
    x *= y;
    return ((y << 63) >> 63) ^ x;
}

3.59

∵ x = 2^64 * xh + xl; y = 2^64 * yh + yl;
∴ x * y = 2^128 * xh * yh + 2^64 * xh * yl + 2^64 * yh * xl + xl * yl
因为是128位,所以(x * y) mod 2^128 才是正解
∴ x * y = 2^64 * xh * yl + 2^64 * yh * xl + xl * yl

rdi = dest; rsi = xl; rdx = yl;
cqto 是将 rax 符号扩展到 rdx : rax,即 yh : yl;而 xh : xl 是用 rcx : rsi 来表示的;

store_prod:
    movq   %rdx, %rax   # rax = yl
    cqto                # 符号扩展 rdx : rax = yh : yl
    movq   %rsi, %rcx   # rcx = xl
    sarq   $63,  %rcx   # 符号扩展 rcx : rsi = xh : yl;与cqto功能相同
    imulq  %rax, %rcx   # rcx = xh * yl
    imulq  %rsi, %rdx   # rdx = yh * xl
    addq   %rdx, %rcx   # rcx = xh * yl + yh * xl
    mulq   %rsi         # rdx : rax = xl * yl;
                        # mul是无符号运算,因为此时 x 和 y 都是 128 位的值,符号在高位上,低位运算不应使用有符号运算;
                        # 注意这里 xl * yl 可能产生向高位的进位
    addq   %rcx, %rdx   # rdx = rdx + xh * yl + yh * xl
                        # 因为可能有低位进位,所以不直接使用 rcx 作为结果的高位,而是要加上 rdx
    movq   %rax, (%rdi) # 将 rax 的值放到结果的低位
    movq   %rdx, 8(%rdi)# 将 rdx 的值放到结果的高位
    ret

3.60

loop:
    movl  %esi, %ecx # ecx = esi
    movl  $1, %edx   # edx = 1 = mask
    movl  $0, %eax   # eax = 0 = result
    jmp   .L2
.L3:
    movq  %rdi, %r8  # r8 = x
    andq  %rdx, %r8  # r8 = r8 & rdx = x & mask
    orq   %r8, %rax  # rax = rax | r8 = result | (x & mask)
    salq  %cl, %rdx  # rdx = rdx << cl = mask << (n & 0xFF)
.L2:
    testq %rdx, %rdx
    jne   .L3        # if rdx != 0 -> jmp L3
    rep; ret

A. rdi = x; rsi = n; rax = reault; rdx = mask
B. result = 0; mask = 1;
C. mask != 0
D. mask << (n & 0xFF)
E. result | (x & mask)
F.

long loop(long x, int n)
{
    long result = 0;
    long mask;
    for(mask = 1;mask != 0;mask = mask << (n&0xFF)){
        result |= x & mask;
    }
    return result;
}

3.61

不要在判断指针是否为空之前就解引用

long cread_alt(long *xp) {
    long zero = 0;
    return *(xp ? xp : (&zero));
}

3.62

很简单,不要忘记 break

case MODE_A:
    result = *p2;
    *p2 = *p1;
    break;
case MODE_B:
    result = *p1 + *p2;
    *p1 = result;
    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;

3.63

简单的 switch 和跳转表,只需注意 break 和 顺延

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:
        x = x * 15;
    case 65:
        x *= x;
    case 61:
    default:
        result = x + 75;
    }
    return result;
}

3.64

A.
扩展到三维:
定义 T D[R][S][T];
&D[i][j][k] = xa + L(S * T * i + T * j + k)

B.
根据公式,知索引为 S * T * i + T * j + k;
则 R = 3640/5/13/8 = 7;S = 65/13 = 5;T = 13

3.65

根据 rdx 和 rax 每次循环的递增值即可判断
A. rdx
B. rax
C. M = 120/8 = 15

3.66

sum_col:
    leaq   1(, %rdi, 4), %r8        # r8 = 4 * n + 1
    leaq   (%rdi, %rdi, 2), %rax    # rax = 3 * n
    movq   %rax, %rdi               # rdi = rax = 3 * n
    testq  %rax, %rax
    jle    .L4                      # if rax == 0 -> jmp L4
    salq   $3, %r8                  # r8 = 8 * (4 * n + 1)
    leaq   (%rsi, %rdx, 8), %rcx    # rcx = A + 8 * j = A[0][j]
    movl   $0, %eax                 # result = 0
    movl   $0, %edx                 # i = 0
.L3:
    addq   (%rcx), %rax             # result = result + A[i][j]
    addq   $1, %rdx                 # i += 1
    addq   %r8, %rcx                # 每次加8 * (4n + 1),说明每一行有 4n + 1 个元素,因此 NC(n) 为 4n + 1
    cmpq   %rdi, %rdx               
    jne    .L3                      # rdx != 3*n 就继续循环,因此 NR(n) 为 3 * n
    rep; ret
.L4:
    movl $0, %eax
    ret

宏定义如下:

#define NR(n) (3 * (n))
#define NC(n) (4 * (n) + 1)

3.67

A.
表格中的地址指的是相对于 eval 的原始栈顶的地址,自上向下递减的

相对地址 栈值 当前栈顶
-104 x <—–
-96 y
-88 &z
-80 z
垃圾值
0 eval 的原始栈顶

B.
传递了整个结构体 strA,包括 x y &z

C.
使用 rsp 加偏移访问

相对地址 栈值 当前栈顶
-112 返回地址 <—–
-104 x
-96 y
-88 &z
-80 z
垃圾值
-40 u[0]
-32 u[1]
-24 q
垃圾值
0 eval 的原始栈顶

D.
使用 rdi 加偏移访问

E.
同样是使用 rsp 加偏移访问

相对地址 栈值 当前栈顶
-104 x <—–
-96 y
-88 &z
-80 z
垃圾值
-40 u[0]
-32 u[1]
-24 q
垃圾值
0 eval 的原始栈顶

F.
由于结构体的复杂性,是通过存储在栈上访问的

3.68

做这个题时应注意 数据对齐
对齐原则:任何 K 字节的基本对象的地址必须是 K 的倍数

setVal:
    movslq  8(%rsi), %rax   # 为使 int 四字节对齐,可得 5 <= B <= 8
    addq    32(%rsi), %rax  # 为使 long 八字节对齐,7 <= A <= 10
    movq    %rax, 184(%rdi) # 180 <= A * B * 4 <= 184
    ret

解得 A=9 B=5

3.69

<test>:
    mov    0x120(%rsi), %ecx        # ecx = *(bp + 288)
    add    (%rsi), %ecx             # ecx += *bp
                                    # 上两行可推断 288 是 last 与 first 的首地址之差
    lea    (%rdi, %rdi, 4), %rax    # rax = 5i
    lea    (%rsi, %rax, 8), %rax    # rax = bp + 40i
    mov    0x8(%rax), %rdx          # rdx = *(bp + 40i + 8)
    movslq %ecx, %rcx               # rcx = ecx(符号扩展)
                                    # ecx = n,将其符号扩展,赋值给 x 
                                    # 由此推断 a_struct 中的 x 是长整型 long 的数组
    mov    %rcx, 0x10(%rax, %rdx, 8)# 8 * (*(bp + 40i + 8)) + bp + 40i + 16 = rcx
    retq

比较简单的推断在上图中列出;
难点在mov 0x8(%rax), %rdx # rdx = *(bp + 40i + 8)mov %rcx, 0x10(%rax, %rdx, 8)# 8 * (*(bp + 40i + 8)) + bp + 40i + 16 = rcx,bp + 8 + 40i 很容易猜出它是 b_struct 中 a[i] 的首地址,且 b_struct 八字节对齐,结构 a_struct 的字节数为 40;
而 8 * ( *(bp + 40i + 8) ) 可以知道这是索引,进而推断 a_struct 中 idx 排在 x 前面;
那么 bp + 40i + 16 就可以写成 bp + 8 + 40i + 8,前一个 8 是 first 的偏移,后一个 8 是 idx 的偏移;
A.
根据推断,结构 a_struct 的字节数为 40
CNT = (288 - 8)/40 = 7
B.

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

猜你喜欢

转载自blog.csdn.net/one_of_a_kind/article/details/81738501
今日推荐