CSAPP深刻理解計算機系統(第二版)第三章家庭做業答案

《深刻理解計算機系統(第二版)》CSAPP 第三章 家庭做業

這一章介紹了AT&T的彙編指令 比較重要 本人完成了《深刻理解計算機系統(第二版)》(如下簡稱CSAPP)第三章的家庭做業,並與網上的一些答案進行了對比修正。app

感謝博主summerhust的整理,如下貼出AT&T經常使用匯編指令函數

AT&T經常使用匯編指令

數據傳送指令oop

指令 效果 描述
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。.net

條件碼:指針

條件碼寄存器描述了最近的算數或邏輯操做的屬性。code

CF:進位標誌,最高位產生了進位,可用於檢查無符號數溢出。blog

OF:溢出標誌,二進制補碼溢出——正溢出或負溢出。get

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

int decode2(int x,int y,int z){
    int temp1 = z-y;
    int temp2 = temp1<<15;
    temp2 = temp2>>15;
    return (x^temp1)*temp2;
}

3.55

ll_t is defined as long long

void store_prod(ll_t *dest, ll_t x, int y){
    *dest = x*y;
}

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

movl 12(%ebp),%esi  ;get x(long long)的低位
movl 20(%ebp),%eax  ;get y(int)
movl %eax,%edx      ;備份y
sarl $31,%edx       ;獲取y的符號位
movl %edx,%ecx      ;備份y的符號位
imull %esi,%ecx     ;x的低位無符號乘法乘以y的符號位(0或-1 即 全0或全1)
movl 16(%ebp),%ebx  ;get x(long long)的高位
imull %eax,%ebx     ;x的高位乘以y
addl %ebx,%ecx      ;%ecx=x高位*y+x低位*y的符號位(補全下面無符號的缺乏部分)
mull %esi           ;%edx=(x的低位*y)的高位 %eax=(x的低位*y)的低位(這一步無符號)
leal (%ecx,%edx),%edx;%edx = %ecx+%edx
movl 8(%ebp),%ecx   ;get dest
movl %eax,(%ecx)    ;dest[0] = (x的低位*y)的低位
movl %edx,4(%ecx)   ;dest[1] = (x的低位*y)的高位+x的高位*y+x的低位*y的符號位(0或者-1)
32位機器模擬64位機器的有符號乘法
x表示 long long x 的值
x = xh*2^32 + xl
x*y = y*xh*2^32 + y*xl 完整形式是96位長 但僅須要64位
所以咱們須要y*xl的完整64位和y*xh的低32位 並分開存儲

3.56

一個知識點:位移操做能夠是一個當即數或者是存放在單字節寄存器元素%cl中

A.
x:%esi,n:%ebx,result:%edi,mask:%edx
B.
result=0x55555555; //‭0101 0101 0101 0101 0101 0101 0101 0101‬
mask=0x80000000;
C.
mask!=0
D.
mask每次循環右移n位
E.
result每次循環和x&mask的值進行異或
int loop(int x,int n){
    int result = 0x55555555;
    int mask;
    for(mask = 0x80000000;mask!=0;mask=(unsigned)mask>>n){//須要注意邏輯右移 將有符號數轉化爲無符號
        result ^= mask&x;
    }
    return result;
}

3.57

int cread_alt(int *xp){
    int temp = 0;
    int *p = xp?xp:&temp;
    return *p;
}

3.58

typedef enum {MODE_A, MODE_B, MODE_C, MODE_D, MODE_E} mode_t;
int switch3(int *p1, int *p2, mode_t action){
    int result = 0;
    switch(action){
        case MODE_A:
            result = *p1;
            *p1 = *p2;
            break;
        case MODE_B:
            result = *p1+*p2;
            *p2 = result;
            break;
        case MODE_C:
            *p2 = 15;
            result = *p1;
        case MODE_D:
            *p2 = *p1;
        case MODE_E:
            result = 17;
            break;
        default:
            result = -1;
            break;
    }
    return result;
}

選一個部分的彙編代碼註釋解釋一下

.L14(MODE_B):
movl 12(%ebp),%edx    ;get p2 reuslt = p2
movl (%edx),%eax      ;temp_p2 = p2
movl %eax,%edx        ;result = *p2
movl 8(%ebp),%ecx     ;get p1 temp_p1 = p1
addl (%ecx),%edx      ;result += *p1此時result = *p1+*p2
movl 12(%ebp),%eax    ;get p2 
movl %edx,(%eax)      ;*p2 = result
jmp .L19              ;

3.59

int swith_prob(int x, int n){
    int result = x;
    switch(n){
        case 40:
        case 42:
            result <<= 3;
            break;
        case 43:
            result >>= 3;
            break;
        case 44:
            result <<= 3;
            result -= x;
        case 45:
            result *= result;
        case 41:
        default:
            retult += 17;
            break;
    }
    return result;
}

解釋和部分彙編代碼註釋

從gdb打印出來的內容能夠看出40的狀況和42是同樣的所以40內容爲空,其後緊跟42
44跳轉至後緊接着執行了45跳轉的內容 45以後也無跳轉 所以44和45沒有break
41的狀況與default相同所以41置爲空寫在defaul前
mov 0xc(%ebp),%eax  ;%ebp+12的位置獲取第二個參數,即n
sub 0x28,%eax       ;n-40(0x28爲16進制 轉爲10進製爲2*16+8=40)
cmp 0x5,%eax        ;n-40 與 5
ja 8048435(switch_pro+0x15);n-40-5>0? 即n若大於45直接返回

3.60

A.從彙編代碼看出
A[i][j][k] = A+(63i+9j+k)*4
B.
解決方程T=9, S*T=63, R*S*T*4=2772
T = 9
S = 7 
R = 11

3.61

int var_prod_ele(int n, int A[n][n], int B[n][n], int i,int k){
    int result = 0;
    int *a_l = &A[i][0];
    int *a_r = &A[i][n];
    int *b_l = &b[0][k];
    while(a_l!=a_r){
        result += (*a_l)*(*b_l);
        b_l += n;
        a_l++;
    }
    return result;
}

可自行查看彙編代碼驗證

L3:
movl (%ebx), %ecx
imull (%edx), %ecx
addl %ecx, %eax
addl %edi, %ebx
addl $4, %edx
cmpl %edx, %esi
jne L3

3.62

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

使用指針進行優化

void transpose(int A[M][M]){
    int i,j;
    for(i=0;i<M;i++){
        int *temp1 = &A[i][0];
        int *temp2 = &A[0][i];
        for(j=0;j<i;j++){
            int t = *temp1;
            *temp1 = *temp2;
            *temp2 = t;
            temp1++;
            temp2 += M;
        }
    }
}

3.63

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

3.64

A.
8(%ebp)  result
12(%ebp) s1.p
16(%ebp) s1.v

B.
------------%ebp
s2.sum
s2.prod
s1.v
s1.p
&s2
------------%esp

C.
將結構體變量的各個成員的值傳入函數

D.
將返回變量的地址傳遞出去

3.65

A=3,B=7

3.66

這個題看了很久,不得不佩服GCC

寫下詳細註釋

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

其實分析出大小就能作這個題 並不須要徹底看懂彙編代碼

3.67

A.
e1.p:0
e1.x:4
e2.y:0
e2.next:4
B.8
C.
void proc(union ele *up)
{
    up->e2.next->e1.x=*(up->e2.next->e1.p) - up->e2.y;
}

3.68

void good_echo()
{
    char c;
    int x = 0;
    while( x=getchar(), x!='\n' && x!=EOF)
    {
        putchar(x);
    }
}

3.69

long trace(tree_ptr tp){
    long result = 0;
    while(tp){
        result = tp->val;
        tp = tp->left;
    }
    return result;
}

輸出二叉樹最左邊節點的值

3.70

long traverse(tree_ptr tp){
    if(!tp) return 9223372036854775807;
    long v = tp->val;
    long left = traverse(tp->left);
    long result = traverse(tp->right);
    if(left <= result) result = left;
    if(v <= result) result = v;
    return result;
}

或者換一種寫法

long traverse(tree_ptr tp){
    long result =  9223372036854775807;
    if(tp){
        long lv = traverse(tp->left);
        long rv = traverse(tp->right);
        result = lv <= rv ? lv : rv;
        result = result > tp->val ? tp->val : result;
    }
    return result;
}

求二叉樹節點最小值

指令 效果
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

相關文章
相關標籤/搜索