Appearance
题目
在按字节编址的计算机 M 上,题 43 中 f1 的部分源程序(阴影部分)与对应的机器级代码(包括指令的虚拟地址)如下图所示。
int f1(unsigned n)
1 00401020 55 push ebp
... ... ...
for (unsigned i = 0; i <= n-1; i++) {
... ... ...
20 0040105E 39 4D F4 cmp dword ptr [ebp-0Ch],ecx
... ... ...
power *= 2;
... ... ...
23 00401066 D1 E2 shl edx,1
... ... ...
return sum;
... ... ...
35 0040107F C3 ret其中,机器级代码行包括行号、虚拟地址、机器指令和汇编指令。
请回答下列问题。
(1) 计算机 M 是 RISC 还是 CISC?为什么?
(2) f1 的机器指令代码共占多少字节?要求给出计算过程。
(3) 第 20 条指令 cmp 通过 i 减 n-1 实现对 i 和 n-1 的比较。执行 f1(0) 过程中,当 i=0 时,cmp 指令执行后,进/借位标志 CF 的内容是什么?要求给出计算过程。
(4) 第 23 条指令 shl 通过左移操作实现了 power2 运算,在 f2 中能否也用 shl 指令实现 power2?为什么?
解析
本题在 2017-43 的 f1 函数之上,给出 x86 反汇编片段,考查 CISC vs RISC 的判别、机器代码段长度计算、cmp 后标志位推导、以及 整数移位不能套用到浮点数上 这一陷阱。
(1) M 是 RISC 还是 CISC?[2 分]
M 为 CISC。
判据: RISC 的核心特征是 指令定长(如 MIPS / RISC-V 32 位定长,ARM Thumb 16 位定长)。题表中 M 的指令长度参差不齐:
push ebp:1 字节(55H);cmp dword ptr [ebp-0Ch], ecx:3 字节(39 4D F4H);shl edx, 1:2 字节(D1 E2H)。
变长指令是 CISC(如 x86)的典型特征。配合"ebp / ecx / edx 等 32 位通用寄存器名称",可锁定为 x86(IA-32),是 CISC。
编者注(生僻术语): RISC vs CISC 的判别还有别的角度——指令数(RISC 少、CISC 多)、寻址方式(RISC 简单几种、CISC 多达十余种)、是否 load-store 架构(RISC 是、CISC 不一定)。指令长度是最直观的一个。
(2) f1 的机器代码总字节数 [2 分]
f1 第一条指令地址:0040 1020H(push ebp); 最后一条指令地址:0040 107FH(ret)。
ret 指令长度 = 1 字节(机器码 C3H),所以 f1 占据的地址区间是 [1020H, 1080H):
易错点: "+1" 别忘——字节范围 [a, b] 的长度是 b − a + 1,闭区间。漏掉就是 95B。
(3) 执行 f1(0) 时 cmp 后 CF 的值 [3 分]
Step 1. 算 cmp 的两个操作数。
执行 f1(0) 时 n = 0,n - 1 在 unsigned 算术下溢回最大值:
cmp 第一轮(i = 0):
Step 2. 用补码加法实现减法。
减法 = 加被减数补码:
实际加法器做:
Step 3. 求 CF。
加法器的进位输出 (最高位 0+0+0 没产生进位)。x86 减法约定下:
Step 4. 物理意义验证。
0 < FFFFFFFFH(无符号),借位发生 → CF = 1。✓
易错点: x86 的 CF 在减法里是"借位标志",规则与"加法的 Cout 直接做 CF" 相反。记忆法:减法 CF = 1 ↔ 被减数 < 减数(当作无符号)。
(4) f2 中能否用 shl 实现 power * 2?[3 分]
不能。
原因: shl 把整数的位作为整体左移 1 位(最低位补 0),等价于"乘 2"——但这只对 整数补码 / 无符号 表示成立。
float 的机器数不是单一权值的二进制串,而是 IEEE 754 编码:
[符号 S | 阶码 E | 尾数 M]
1 位 8 位 23 位整体左移会把:
- 符号位移出(数据丢失),相邻的阶码最高位"补"进符号位 → 数被强行变正/变负;
- 阶码错位 → 数量级跳变到完全无关的数;
- 尾数最低位被填 0(这倒是无害)。
要让浮点数 ×2,正确做法是 阶码字段加 1——这是浮点 ×2 的硬件捷径,但 shl 不知道这种结构,所以失败。
编者注(生僻术语): x86 上浮点的乘法走 FPU / SSE 指令(如
mulss、fmul),不是整数移位。现代编译器会发现power *= 2的右操作数是 2 的幂,对 int 优化成shl,但对 float 维持mulss——它们的机器表示根本不同。