int cread_alt(int *xp)
{
int t = 0;
int *p = xp?xp:&t;
return *p;
}
3.58
MODE_A: result = *p1; *p1 = *p2; break;
MODE_B: result = *p1 + *p2; *p2 = result; break;
MODE_C: result = *p1; *p2 = 15; break;
MODE_D: *p2 = *p1;
MODE_E: result = 17; break;
default: result = -1; break;
3.59
int switch_prob(int x, int n)
{
int result = x;
switch(n)
{
case 0x28:
case 0x2a:
result <<= 3; break;
case 0x2b:
result >>= 3; break;
case 0x2c:
result <<= 3;
result -= x;
case 0x2d:
result *= result;
case 0x29: //也可以不要
default:
result += 0x11;
}
return result;
}
中间有一句话没明白,汇编第12行 lea 0x0(%esi), %esi
3.60
对于A[R][S][T],A[i][j][k] 的位置是 A(,i*S*T+j*T+k,4)。
由汇编代码可知:
S*T = 63;
T = 9;
R*S*T = 2772/4;
所以得 R=11, S=7, T=9。
3.61
感觉可以用--j,而不是比较j和n。
int var_prod_ele(int n, int A[n][n], int B[n][n], int i, int k)
{
int j = n-1;
int result = 0;
for(; j!=-1; --j)
result += A[i][j] * B[j][k];
return result;
}
但是这样得到的结果仍然会使用到存储器。
按下面的代码,循环里面貌似就没有用到存储器。
但是用到了一个常量4,就是增加a的时候,会add 4。
只需要result,a,e,b,4n这五个变量。
int var_prod_ele(int n, int A[n][n], int B[n][n], int i, int k)
{
int result = 0;
int *a = &A[i][0];
int *b = &B[0][k];
int *e = &A[i][n];
for(;a!=e;)
{
result += *a * *b;
b+=n;
a++;
}
return result;
}
下面是其汇编代码的循环部分:
edi是4*n,ebx和edx分别是b和a,esi是e,eax是result。
ecx是用于存储乘法的寄存器。
L4:
movl (%ebx), %ecx
imull (%edx), %ecx
addl %ecx, %eax
addl %edi, %ebx
addl $4, %edx
cmpl %edx, %esi
jneL4
我怎么感觉前面那个程序,编译器应该也会自动优化。。。
3.62
M = 76/4 = 19;
i在edi中,j在ecx中;
int transpose(int M, int A[M][M])
{
int i,j;
for(i=0; i {
int *a = &A[i][0];
int *b = &A[0][i];
for(j=0; j {
int t = *a;
*a = *b;
*b = t;
++a;
b += M;
}
}
}
3.63
E1(n)在esi中,esi = 3n。
E2(n)在ebx中,ebx = 4*E2(n) = 4*(2n-1)。
所以E2(n) = 2n-1。
3.64
这道题比较考验对知识的拓展应用能力。
根据简单的推测,我们可以知道,imull的两个对象是 ebx和edx,最后edx移动到了(eax)中,所以ebx和edx一个是 *s1.p,一个是s1.v,并且word_sum的12行的eax是result的prod的地址,也就是result的地址。而eax只在第5行赋值,所以result的地址是在8(%ebp)中的。也就是说,结构体返回值实际上是利用类似参数的变量进行传入(在8(%ebp)),而传入的是返回结构体变量的地址。
所以:
A.
8(%ebp)为result的地址。
12(%ebp)为s1.p。
16(%ebp)为s1.v。
B.栈中的内容如下表,分配的20个字节用黄底展示(每一行为4个字节)
y |
x |
返回地址 |
保存的ebp(也是当前ebp的指向) |
s2.sum |
s2.prod |
s1.v |
s1.p |
&s2 (word_sum的返回值地址) |
|
在汇编中,没懂word_sum 15: ret $4
以及diff 12: subl $4, %esp的意义何在。
可能是为了清除那个result的返回地址。
C.
传递结构体参数就像正常的传值。结构体的每一个变量可以看做是单独的参数进行传入。
D.
返回结构体的通用策略:将返回变量的地址看做第一个参数传入函数。而不是在函数中分配栈空间给一个临时变量,因为eax确实存不下一个结构体,eax充当返回变量的指针的角色。
3.65
B取四的倍数的上整数 = 8。
8+4+ (B*2)取四的倍数的上整数 = 28。
所以B的可选值为8和7。
2*A*B取四的上整数为44,所以A*B的可选值为21和22。
所以 A=3, B=7。
3.66
我们用结构体A表示a_struct。
首先,根据第11和12行,可以得到 CNT*size(A) = 196。
根据13行,知道 ecx + 4*edx + 8为 ap->x[ap->idx]的地址。
ecx存储的是bp(地址)。
ap的地址是 bp + 4 + i*size(A)
我们知道,ap->x[0] 的地址是 bp + 4 + i*size(A) + pos(x),pos(x)为结构体A中x的偏移。
那么ap->x[ap->idx] 的地址是 bp + 4 + i*size(A) + pos(x) + 4*(ap->idx)。
所以 4*edx + 8 = 4 + i*size(A) + pos(x) + 4*(ap->idx)。
所以,不难猜测,pos(x)=4,也就是说,在A中,首先是idx,再是x数组。
那么,我们看ap->idx在哪里计算过。
到第10行,edx的结果是 7i + bp[4 + 28*i],
bp[4 + 28*i]是什么呢?它很可能是bp中的a[i]的首地址。
我们先这样猜测,于是size(A) = 28,并且bp[4+28*i]的值为ap->idx。
另一方面:4*edx = 28*i + 4*bp[4+28*i] = i*size(A) + 4*(ap->idx)。
所以,我们的猜想是正确的。
因此,size(A) = 28,里面包含了一个int idx和一个数组int x[6]。
总共有多少个A呢?CNT = 196/size(A) = 7。
3.67
A.
e1.p: 0
e1.x: 4
e2.y: 0
e2.next: 4
B.
总共需要8个字节。
C.
不难知道,赋值前后都应该是整数。
edx就是参数up(一个指针)。
最后结果是用eax - (edx)得到的,说明(edx)是整数,即up->___ 为整数,肯定是表示的e2.y。
再看看之前的eax,eax是由(eax)所得,说明到第3行后,eax是个指针。
它是由(ecx)得到的,说明ecx在第二行也是个指针。
而ecx是通过*(up+4)得到的,所以ecx是一个union指针next,即up->e2.next;
到第三行,eax为*(ecx),且是一个指针,所以eax在第三行为int* p,即up->e2.next->e1.p。
所以,赋值符号后面的表达式就为 *(up->e2.next->e1.p) - up->e2.y
再看看前面。
最终赋值的地址是 ecx+4,而ecx那时候是一个next指针,而(next+4)必须是一个int,也不难推测它是e1.x。因此前面就为 up->e2.next->e1.x。
结果如下:
void proc(union ele *up)
{
up->e2.next->e1.x = *(up->e2.next->e1.p) - up->e2.y;
}
3.68
版本一:使用getchar
void good_echo()
{
char c;
int x = 0;
while( x=getchar(), x!='\n' && x!=EOF)
{
putchar(x);
}
}
版本二:使用fgets
void good_echo()
{
const int BufferSize = 10;
char s[BufferSize];
int i;
while(fgets(s, BufferSize, stdin)!=NULL)
{
for(i=0;s[i];++i)
putchar(s[i]);
if(i1) break;
}
return;
}
两种方法对于EOF好像没效果,就是输入一定字符后不按回车直接按EOF,没能正确输出。
网上查到的资料说,getchar在输入字符后,如果直接按EOF,并不能退出,只能导致新一轮的输入。
需要在最开始输入的时候按,即按了回车之后按。
而fgets就不知道了,不按回车,就不添加0。
3.69
long trace(tree_ptr tp)
{
long ret = 0;
while(tp != NULL)
{
ret = tp->val;
tp = tp->left;
}
return ret;
}
作用是从根一直遍历左子树,找到第一个没有左子树的节点的值。
3.70
long traverse(tree_ptr tp)
{
long v = MAX_LONG;
if(tp != NULL)
{
v = min(traverse(tp->left), traverse(tp->right));
//Line16 cmovle: if(r12
v = min(v, tp->v); //Line20 cmovle: if(rax>rbx) rax=rbx;
}
return v;
}
当然,如果要用三目条件表达式的话:
long traverse(tree_ptr tp)
{
long v = MAX_LONG, rv, lv;
if(tp != NULL)
{
lv = traverse(tp->left);
rv = traverse(tp->right);
v = lv < rv ? lv : rv ; //Line16 cmovle: if(r12
v = v > tp->v ? tp->v : v ; //Line20 cmovle: if(rax>rbx) rax=rbx;
}
return v;
}
函数的目的是找到树的所有节点的值中最小的一个。