*/
int decode2(int x ,int y, int z)
{
int a = z - y;
int b = (a << 15) >> 15;
return (x ^ a) * b;
}
// 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;
}
/*
***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;