不完美笔记
首页项目归档照片墙音乐说说杂谈友链关于
封面

计算机原理实验:常量乘除法的汇编实现

写作时间:2026-09-29 11:00:00
# 计算机原理
# 汇编
# 逆向
# IDA
# 实验笔记

实验目的

理解一个基本问题:当乘数或除数是常量时,编译器到底做了什么?

我们写的 C 代码是 a * 3、a / 9 这种形式, 但 CPU 执行时并不一定是「乘法指令」和「除法指令」。

这个实验就是用 IDA 把编译结果翻出来,看看编译器背地里做了什么手脚。

实验环境:Windows + VS2022 + IDA


实验内容

乘法:a * 3

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
    int a = 0;
    printf("请输入数字\n");
    scanf("%d", &a);
    int b = a * 3;
    printf("%d*3=%d", a, b);
    return 0;
}

除法:a / 9

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
    int a = 0;
    printf("请输入数字\n");
    scanf("%d", &a);
    int b = a / 9;
    printf("%d/9=%d", a, b);
    return 0;
}

更极端的两个:a / 7 与 a * 49

unsigned int div_data(unsigned int a){
    return a / 7;
}

unsigned int mul_data(unsigned int a){
    return a * 49;
}

用 IDA 打开后,把这几处的汇编指令抄录下来对比。


背后的原理

这部分是我做完实验后补的笔记,理解了它,实验里看到的那些奇怪的指令就说得通了。

为什么乘法不用 mul

x86 的 mul / imul 指令延迟较高,而 lea(Load Effective Address)虽然名字上是"取地址", 实际上可以做加法、乘小常数、加偏移的组合运算,且不改变标志位,流水线友好得多。

所以 a * 3 常常被编译成类似:

lea eax, [eax+eax*2]    ; eax = eax + eax*2 = eax*3

一条指令搞定,比 mul 快。

a * 49 则可以拆成 49 = 7 * 7 或 49 = 64 - 15 之类的组合, 用移位加加减来实现。实验里那个 a * 49 就是被优化过的典型例子。

除法为什么更麻烦

除法指令 div / idiv 是 x86 里最慢的整数指令之一,延迟可达几十个时钟周期。

而除以一个常量时,编译器可以用一个经典技巧:乘以"魔数",再右移。

原理是:

a / d  ≈  (a * M) >> s

其中 M 和 s 是编译器预先算好的常数。因为无符号除法可以这样精确等价, 所以 a / 7 很可能被编译成一条 imul 加一条 shr——比 idiv 快一个数量级。

这也是为什么实验里 div_data 明明写的是除法, 反汇编里看到的却可能是乘法指令——不是看错了,是真的没有除法。

有符号 vs 无符号的差别

这一点在复习时也强调过:

  • 加减法:因为采用补码表示,有符号与无符号的运算逻辑是一致的,硬件不需要区分
  • 乘除法:必须区分,因为要处理符号扩展和舍入方向

所以编译器为 int a / 7 和 unsigned a / 7 生成的代码是不一样的—— 有符号版本还要额外处理负数向下取整的边界情况。


实验总结

这个实验本身代码量很小,但信息量不小:

  1. 编译器是个激进的优化者。你写的是"乘法",它生成的可能是移位和加法; 你写的是"除法",它生成的可能是乘法和移位。
  2. "可读性"和"可执行性"是两回事。高级语言的一个运算符, 底层可能完全没有对应的指令。
  3. 理解了这一点,再看逆向出来的汇编,就不会被那些"莫名其妙的乘法和移位"困惑了—— 那其实是编译器在替你省时间。

对我个人来说,最直接的收获是:以后看到 imul + shr 的组合, 第一反应应该是"这里原本可能是个除法"。


📌 本文由大学时期的课程实验报告整理而来,已移除姓名、学号、班级、指导教师等个人信息。 原始报告中的截图因可能包含个人环境信息(文件路径、用户名)未一并收录。

avatar

Winter21c

暗处提灯,静处守门;功成不必在我,安宁自在人间

RECOMMENDED

我是如何修改她的 blog 的

2026-09-29 10:45:00

红米 K20 Pro 的 Arch Linux 移植手记

2026-10-02 17:25:00

计算机原理作业:直接映射高速缓存的两道题

2026-09-29 11:15:00

Table of Contents