Skip to content

2019年 408 计算机组成原理 第 45 题

计算机组成原理2019年综合题16分

题目

已知 ,计算 的 C 语言函数 f1 的源程序(阴影部分)及其在 32 位计算机 M 上的部分机器级代码如下:

int f1(int n) {
1   00401000   55             push ebp
      ...           ...            ...
   if(n>1)
11  00401018  83 7D 08 01    cmp   dword ptr [ebp+8],1
12  0040101C  7E 17          jle   f1+35h (00401035)
   return n*f1(n-1);
13  0040101E  8B 45 08       mov   eax, dword ptr [ebp+8]
14  00401021  83 E8 01       sub   eax, 1
15  00401024  50             push  eax
16  00401025  E8 D6 FF FF FF call  f1 ( 00401000)
      ...           ...            ...
19  00401030  0F AF C1       imul  eax, ecx
20  00401033  EB 05          jmp   f1+3Ah (0040103a)
   else return 1;
}
21  00401035  B8 01 00 00 00 mov   eax,1
      ...           ...            ...
26  00401040  3B EC          cmp   ebp, esp
      ...           ...            ...
30  0040104A  C3             ret

其中,机器级代码行包括行号、虚拟地址、机器指令和汇编指令,计算机 M 按字节编址,int 型数据占 32 位。请回答下列问题:

(1) 计算 f(10) 需要调用函数 f1 多少次?执行哪条指令会递归调用 f1?

(2) 上述代码中,哪条指令是条件转移指令?哪几条指令一定会使程序跳转执行?

(3) 根据第 16 行的 call 指令,第 17 行指令的虚拟地址应是多少?已知第 16 行的 call 指令采用相对寻址方式,该指令中的偏移量应是多少(给出计算过程)?已知第 16 行的 call 指令的后 4 字节为偏移量,M 是采用大端方式还是采用小端方式?

(4) f(13)=6227020800,但 f1(13) 的返回值为 1932053504,为什么两者不相等?要使 f1(13) 能返回正确的结果,应如何修改 f1 的源程序?

(5) 第 19 行的 imul 指令(带符号整数乘)的功能是 R[eax]←R[eax]×R[ecx],当乘法器输出的高、低 32 位乘积之间满足什么条件时,溢出标志 OF=1?要使 CPU 在发生溢出时转异常处理,编译器应在 imul 指令后应加一条什么指令?

解析

本题考查 递归函数的机器级理解:把汇编代码翻成"函数调用 + 跳转"流程,并涉及 call 指令的相对寻址、字节序、整数溢出、溢出陷阱 等几个细节。

阅读机器代码先抓 三类跳转

  • jle / je / jne 等条件转移;
  • call / ret 函数调用 / 返回;
  • jmp 无条件跳转。

(1) 调用次数与递归调用指令 [2 分]

f1(10) → f1(9) → ... → f1(1) → 返回 1。链条上一共调用 10 次 f1(最外层一次 + 内部递归 9 次)。

第 16 行 call f1 是递归调用指令——它在 f1 的代码体内又调用 f1 自身。

(2) 条件转移与必跳转的指令 [2 分]

条件转移指令: 第 12 行 jle —— 当上一个比较结果为"小于或等于"(即 ZF = 1 或 SF ≠ OF)时跳转。本题中 jle 跳到 00401035H(即 else return 1 分支),对应 n ≤ 1 的递归出口。

一定会跳转的指令(无条件改变 PC):

指令含义
16call调用 f1 → PC 跳到被调函数地址
20jmp无条件跳过 else 分支到 0040103AH
30ret子程序返回,PC 恢复为调用方下一条

(3) call 后续指令地址、call 偏移量、字节序 [4 分]

Step 1. 第 17 行虚拟地址。

第 16 行 call 的机器码 E8 D6 FF FF FF(5 字节),起始地址 00401025H

Step 2. call 偏移量计算。

x86 的 call 是相对寻址,目标地址 = (call 后续指令地址) + 偏移量(即 PC 在 fetch call 之后已经指向第 17 行):

按 32 位补码减法:

Step 3. 判断字节序。

机器码中偏移字段是 D6 FF FF FF(按内存顺序读出,低字节在前)。如果是小端序,最低有效字节 D6 存放在最低地址,与 FFFF FFD6H 的低字节 D6 对应;如果是大端序,最低地址应存放最高字节 FF

字段顺序 D6 FF FF FF → 最低地址放最低字节 → M 采用小端(little-endian)方式。

编者注(生僻术语): x86 / x86-64 / RISC-V 均为小端序;ARM 默认小端但可切换。区别只在多字节数据在内存中的字节顺序上,不影响算术运算结果。

(4) f1(13) 不正确的原因与修改方法 [2 分]

,超过 32 位 int 上限

f1 在递归乘法过程中发生了 溢出,结果按 mod 截断后再按补码解读 → 1932053504(注意:这是从下方截掉位之后再按 int 重新解读的"看起来正常但其实错"的数)。

修改方法: 把 f1 的返回类型改成 更宽的整数(long long,64 位)浮点(double / long double,使范围足够容纳 13!。同时把内部变量 eax 等也对应宽化。

易错点: 不要简单改成 unsigned int——unsigned 范围 仍小于 。必须升到 64 位整数或浮点。

(5) imul 指令的溢出判定 + 加什么指令转异常 [3 分]

OF = 1 的条件: imul 是带符号整数乘法,输出 64 位结果(高 32 + 低 32)。仅取低 32 位作为返回值时,溢出判定为:

换言之,乘积高 33 位(高 32 位再加上低 32 位的最高位)既不全为 0 也不全为 1 时,OF = 1。原因:带符号截断不溢出当且仅当被截掉的高位是低位符号位的符号扩展。

应加的指令: 在 imul 后插入一条 溢出自陷指令(如 x86 中的 into,或 MIPS 的 tge / RISC-V 的"显式判 OF 后 trap")。它检测 OF 标志,OF = 1 时触发异常 → 进入溢出异常处理程序。

编者注(生僻术语): "自陷(trap)"是软件主动触发硬件异常的一种机制,与中断的区别在于:中断是异步的(外设来的),自陷是同步的(指令自己产生)。x86 的 into(INTerrupt on Overflow)就是 OF=1 时触发 4 号异常。

最后更新:

⚠️ 这道题暂未配可视化,欢迎在 CodeBrick 反馈区告诉我们你想看哪道题