深入理解计算机系统家庭作业第三章

/*
***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;




你可能感兴趣的:(深入理解计算机系统家庭作业)