1. 程式人生 > >深入理解計算機系統家庭作業第三章

深入理解計算機系統家庭作業第三章

/*
***3.54
***寫出decode2的原型

*/

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

/*
***3.55
*/
typedef long long ll_t;


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        //將x的低位存到%esi
movl   20(%ebp), %eax       //將y存到%eax
movl   %eax, %edx   
sarl   $31, %edx                  //將(y >> 31)存到%edx
movl   %edx, %ecx
imull   %esi, %ecx             //計算x_low * (y >> 31)存到%ecx
movl   16(%ebp), %ebx    //將x的高位存到%ebp
imull   %eax, %ebx           //計算x_high * y
addl   %ebx, %ecx           //計算 x_high * y + x_low * (y >> 31) 存到%ecx
mull   %esi                       //計算y * x_low 的無符號64位乘積
leal   (%ecx, %edx), %edx    //將64位乘積的高位與x_high * y + x_low * (y >> 31)得到最終結果的高位
movl   8(%ebp), %ecx
movl   %eax, (%ecx)
movl   %edx, 4(%ecx)          //將結果寫入目的記憶體地址

說明:

該彙編程式碼其實是y擴充套件至64位再進行兩個64位數的乘積然後進行截斷得到的。

事實上有:

y *{signed} x =
(y_high * 2^32 + y_low) *{signed} (x_high * 2^32 + x_low) =
y_high *{signed} x_high * 2^64 +
y_high *{signed} x_low * 2^32 +
y_low *{signed} x_high * 2^32 +
y_low *{signed} x_low(有符號的x_low與無符號的x_low相等,故可用mull指令)

而y_high *{signed} x_high由於乘以2^64,所以對結果不會產生影響。

/*
***3.56寫出loop函式原型
*/

int loop(int x, int n)
{
	int result = 0x55555555;
	int mask;
	for(mask = 0x80000000;mask !=0; mask = (unsigned)mask >> (n & 0xFF))
	{
		result ^= x & mask;
	}
	return result;
}

/*

***3.57用條件傳送指令寫函式cread_alt

*/

int cread_alt(int *xp){
     int a = 0;
     return *(xp ? xp : &t);
}

思路:原來函式中的錯誤就是無論xp是否為NULL,都對其發生了間接引用。將引用符號放在最外面,那麼當xp為NULL時,

條件傳送指令將臨時變數a的地址傳送至%eax,故不會產生對xp的間接引用。下面貼上產生的彙編程式碼:

        subl	$16, %esp
	.cfi_def_cfa_offset 20
	movl	20(%esp), %eax
	movl	$0, 12(%esp)
	leal	12(%esp), %edx
	testl	%eax, %eax
	cmove	%edx, %eax
	movl	(%eax), %eax
	addl	$16, %esp
	.cfi_def_cfa_offset 4
	 ret

/*

***3.58

*/

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;
			break;
		case MODE_D:
			*p2 = *p1;
			result = 17;
			break;
		case MODE_E:
			result = 17;
			break;
		default:
			result = -1;
			break;	
	}
	return result;
}


/*

***3.59根據反彙編程式碼補全c語言程式碼

*/

int switch_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 = (result<<3) - result;
		case 45:
			result *= result;
			result += 17;
			break;
		default:
			result += 17;
			break;
	}
	return result;
}


/*

***3.60

*/

A.  &A[i][j][k] = Xa + L(i * S*T + j * T + K)

B.  根據彙編程式碼可得,9*j存在暫存器%eax中,63*i存在暫存器%edx中,對照公式可得S=7,T=9;

      最後一句movl     $2772    %eax   可知R = 2772/S/T/4 = 11

/*

***迴圈中的值的數量從6個減少到5個

*/

int car_prod_ele(int n,int A[n][n],int B[n][n],int i,int k)
{
	int j;
	int result = 0;

	int *ap = A[i];
	int *bp = &B[0][k];
	
	for(j = 0; j < n; j++)
	{
		result += *ap * *bp;
		ap++;
		bp += n;
	}
	return result;
}

這題沒有想到很好的解法,以上程式碼經測試是將4n放到暫存器%ebp當中,實際上並沒有減少迴圈值的數量,望高人指點。

/*

***3.62

*/

A.    M的值為19

B.    %edi儲存i,%ecx儲存j

C.  

void transpose(int A[M][M]){
	int i,j;
	for(i = 0;i < M;i++)
	{
		int *p1 = &A[0][i];
		for(j = 0;j < i;j++)
		{
			int *p2 = &A[i][0];
			int t = *p1;                 //交換對稱的兩個值
			*p1 = *p2;
			*p2 = t;
			p1 += M;                     //改變兩個元素的指標
			p2 += 1;		
		}
	}
}

/*

***3.63

*/

E1 = 3n

E2 = 2n - 1

說明:%esi中存放的值為3n,再對照18行可知E1 = 3n;

            每次指標的值變化為8n - 4,可知E2 = 2n - 1。

/*

***3.64

*/

A.     8(%ebp)存放返回值result的地址;12(%ebp)存放s1.p;16(%ebp)存放s1.v。

B.     從高到低依次為s2.sum,s2.prod,s1.v,s1.p,&s2

C.     傳遞結構體引數時,結構體各引數從高到低依次壓入棧中

D.     在8(%ebp)處有一個指向返回值結構體的指標,指標的值作為返回地址,根據返回地址寫入值

/*

***3.65

*/

A=3

B=7

首先分析B,t的首地址和u的首地址相差20個位元組,t本身佔4位元組,所以short陣列s[B]佔16個位元組,根據四位元組對齊的原則,可知B=7或8;

short x[A][B]佔44個位元組,當B=8時,A無解,當B=7時,可得A=3.

/*

***3.66

*/

A.   CNT = 7;

B.   struct a_struct{

                int   idx;

                int   x[6];

       }

這個題最難的地方是彙編程式碼的第十行,對照彙編程式碼和C程式碼可知,彙編程式碼的第十二行對應C程式碼的第九行,彙編程式碼的十三行對應C程式碼的十一行。

經過分析(事實上帶有一些猜測),第十行add的第一個運算元就是ap->idx的值,加上7i的原因是a_struct中有7個整型變數(這與我們計算ap->idx的地址

為bp + 28i + 4)也是吻合的;然後我們分析彙編程式碼的十三行,前面的0x8應該看做0x4 + 0x4,第一個0x4是b_struct中left佔的位元組數,第二個0x4是a_struct

中idx佔的位元組數,所以可知idx是結構體第一個變數,另外,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寫一個good_echo函式,對任意長度的輸入行都能工作(即時超過了緩衝區)

*/

#define N 50

void good_echo()
{
	char buf[N];
	while(fgets(buf,N,stdin) != NULL)
	{
		for(int i = 0;i < N;i++)
		{
			if(buf[i] == '1')
				return;
			if(buf[i] == '\0')
				break;
			putchar(buf[i]);
		}
	}

}

/*

***3.69寫出函式trace

*/

A.   給出該函式的C版本,使用while迴圈

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

B.    該函式得到最左邊葉子的val值。

/*

***3.70

*/

A.   生成這個函式的C版本

long traverse(tree_ptr tp)
{
	if(!tp)
		return 0x7fffffffffffffff;
	long min_left = traverse(tp ->left);
	long min_right = traverse(tp ->right);
	long min = min_left < min_right ? min_left : min_right;
	min = min < (tp ->val)? min : (tp ->val);
	return min;
}


B.   這個函式是求樹中val的最小值,若為空樹,則返回0x7fffffffffffffff;