3.58
long decode(long x, long y, long z)
{
y -= z;
x *= y;
return (y << 63 >> 63) ^ x;
}
3.59
3.60
A
x : %rdi
n : %esi
result : %rax
mask : %rdx
B
result = 0
mask = 1
C
mask != 0
D
mask <<= n
E
result |= x & mask;
F
long loop(long x, long n)
{
long result = 0;
long mask;
for (mask = 1; mask; mask <<= n)
{
result |= x & mask;
}
return result;
}
3.61
看到别人的答案有这样写的:
long cread_alt(long *xp)
{
return (!xp) ? 0 : *xp;
}
本来我认为也是对的,因为从汇编层面上看,先计算第一条,结果是0
,再用条件传送指令判断指针是否为空,从而传送0
或者是指向的内容,避免了空指针引用。
然而查询资料得到,条件传送命令不管测试结果为如何,总会两条路径都计算出来,而不是测试结果失败了就不算第二条路径,因此这个代码是有问题的。
答案如下:
long cread_alt(long *xp)
{
long tem = 0;
xp = xp ? xp : &tem;
return *xp;
}
3.62
typedef enum
{
MODE_A,
MODE_B,
MODE_C,
MODE_D,
MODE_E
} mode_t;
long switch3(long *p1, long *p2, mode_t action)
{
long result = 0;
switch (action)
{
case MODE_A:
result = *p2;
*p2 = *p1;
break;
case MODE_B:
result = *p1;
result += *p2;
*p1 = result;
break;
case MODE_C:
*p1 = 59;
result = *p2;
break;
case MODE_D:
*p1 = *p2;
result = 27;
break;
case MODE_E:
result = 27;
break;
default:
result = 12;
}
return result;
}
3.63
long switch_prob(long x, long n)
{
long result = x;
switch (n)
{
case 60:
case 62:
result = x << 3;
break;
case 63:
result = x >> 3;
break;
case 64:
x *= 15;
case 65:
x *= x;
default:
result = x + 75;
}
return result;
}
3.64
A
假设有数组Type A[R][S][T]
,那么A[i][j][k]
的地址为:
B
根据汇编推得:
解得
3.65
A
%rdx
B
%rax
C
3.66
3.67
这里画出的栈帧越上面地址越大,和书上保持一致。
A
栈帧 | 地址 | 说明 |
---|---|---|
... |
%rsp+96 |
栈底 |
... |
... |
未被使用 |
... |
%rsp+64,%rdi |
|
... |
... |
未被使用 |
z |
%rsp+24 |
|
&z |
%rsp+16 |
|
y |
%rsp+8 |
|
x |
%rsp |
栈顶 |
B
传递了%rdi
C
通过%rsp
加上位移量访问
这里有个小细节困惑了我一会,就是调用process
后%rsp
所指向的位置偏移了8
个字节,一开始没想明白,后来想到call
指令会压入返回地址,所以导致%rsp
自动加8
。
D
通过传递的%rdi
加上位移量设置
E
栈帧 | 地址 | 说明 |
---|---|---|
... |
%rsp+96 |
栈底 |
... |
... |
未被使用 |
r.q |
%rsp+80,%rdi+16 |
|
r.u[1] |
%rsp+72,%rdi+8 |
|
r.u[0] |
%rsp+64,%rdi |
|
... |
... |
未被使用 |
z |
%rsp+24 |
|
&z |
%rsp+16 |
|
y |
%rsp+8 |
|
x |
%rsp |
栈顶 |
返回后同样依靠%rsp
加偏移量访问r
的元素。
F
这里的%rdi
可以看成一个指针,在传参前预先留好一定的空间给返回值,传入结构体时什么都不用做,子函数通过%rsp
访问传入的结构体,通过%rdi
指针设置返回的结构体。
3.68
解得:
3.69
这里我犯了个错误,导致思考了比较久。我误认为结构体的对齐是整个大小,其实应该是其中数据大小最大的一个。导致这个偏移量288
怎么凑都凑不出来。正确的想法是结构体数组大小为40*7
,多出来的8
是int
对结构体对齐的结果。
A
B
typedef struct{
long idx;
long x[4];
};
这里的idx
不一定为long
,实际上int
,short
,甚至char
应该都是可以的,原因是对齐。
3.70
A
字段 | 偏移量 |
---|---|
e1.p |
0 |
e1.y |
8 |
e2.x |
0 |
e2.next |
8 |
B
C
union ele
{
struct{
long *p;
long y;
} e1;
struct{
long x;
union ele *next;
} e2;
};
void proc(union ele *up)
{
up->e2.x = *(up->e2.next->e1.p) - up->e2.next->e1.y;
}
3.71
#include <stdio.h>
const int MAX_LENGTH = 16;
void good_echo()
{
char buffer[MAX_LENGTH];
while (fgets(buffer, MAX_LENGTH, stdin))
{
printf("%s", buffer);
}
}
int main()
{
good_echo();
}
3.72
A
不明白啥叫数学语言解释。
用公式写了一下,感觉不太直观,其实分为两部分
-
减去得到,这是给
i
分配的空间 - 减去,这一部分是给数组分配空间,因为要对字节对齐写成这样,在原汇编中体现为:
leaq 30(,%rdi,8), %rax
andq $-16, %rax
B
目的是数组首地址对齐字节。
C
最小 | 最大 | |
---|---|---|
e1 |
16 |
40 |
n |
偶数 |
奇数 |
s1 |
16的倍数 |
16的倍数+1 |
D
对字节对齐。
对字节对齐。
3.73
不太想啃那个英文的说明了,网上搜了搜怎么内嵌汇编就直接写了。
代码附带验证。
#include <iostream>
typedef enum
{
NEG,
ZERO,
POS,
OTHER
} range_t;
range_t find_range(float x)
{
__asm__(
"vxorps %xmm1, %xmm1, %xmm1\n\t"
"vucomiss %xmm0, %xmm1\n\t"
"jp .OTHER\n\t"
"je .ZERO\n\t"
"ja .NEG\n\t"
"movl $2, %eax\n\t"
"jmp .END\n\t"
".OTHER:\n\t"
"movl $3, %eax\n\t"
"jmp .END\n\t"
".ZERO:\n\t"
"movl $1, %eax\n\t"
"jmp .END\n\t"
".NEG:\n\t"
"movl $0, %eax\n\t"
".END:\n\t"
"rep;ret\n\t");
}
range_t find_range2(float x)
{
range_t result;
if (x < 0)
result = NEG;
else if (x == 0)
result = ZERO;
else if (x > 0)
result = POS;
else
result = OTHER;
return result;
}
float u2f(unsigned x)
{
return *(float *)&x;
}
int main()
{
for (unsigned x = 0; x != 0; x++)
{
if (find_range2(x) != find_range(x))
std::cout << "NO\n";
}
std::cout << "YES\n";
}
3.74
心态崩了,第一次知道还有cmovaq
这种东西,书上好像没写过啊?浪费了超多时间。。。
#include <iostream>
typedef enum
{
NEG,
ZERO,
POS,
OTHER
} range_t;
range_t find_range(float x)
{
__asm__(
"vxorps %xmm1, %xmm1, %xmm1\n\t"
"movq $1, %rax\n\t"
"movq $2, %r8\n\t"
"movq $0, %r9\n\t"
"movq $3, %r10\n\t"
"vucomiss %xmm1, %xmm0\n\t"
"cmovaq %r8, %rax\n\t"
"cmovbq %r9, %rax\n\t"
"cmovpq %r10, %rax\n\t");
}
range_t find_range2(float x)
{
range_t result;
if (x < 0)
result = NEG;
else if (x == 0)
result = ZERO;
else if (x > 0)
result = POS;
else
result = OTHER;
return result;
}
float u2f(unsigned x)
{
return *(float *)&x;
}
int main()
{
for (unsigned x = 0; x != 0; x++)
{
if (find_range2(x) != find_range(x))
std::cout << "NO\n";
}
std::cout << "YES\n";
}
3.75
A
使用寄存器对,第一个参数为%xmm0:%xmm1
,第二个为%xmm2:%xmm3
。
A
寄存器对%xmm0:%xmm1
。