简介:《IBM-PC汇编语言程序设计》习题参考答案,面向使用沈美明、温冬婵版教材的计算机专业学生与自学者,用于课后练习对照与知识点查漏补缺。文档按章节收录习题解答,覆盖数制转换、8位补码表示及加减运算、溢出与进位标志判定、位运算规则、ASCII码与字符串编码、存储器物理地址计算等核心内容,并附有部分图解与详细计算过程。整份答案以单个doc文档打包,共1个文件,压缩包大小约524KB,方便直接打开查阅或打印。当前已有114人学习浏览。通过逐题复盘,读者可系统巩固汇编语言的数据表示与寻址方式,提升阅读与编写底层程序所需的逻辑思维能力,尤其适合备考、作业自查及考前集中复习场景。
1. 为什么一本汇编习题答案值得从头读一遍:从十进制到机器码的必修课
很多人在学汇编时,翻开课本第一页就被劝退了:满页的二进制、十六进制、补码、标志位,看起来像天书。但你可能没意识到,汇编语言最折磨人的地方恰恰也是它最值钱的地方——它逼你把“数在机器里到底长什么样”这件事想明白。这本《IBM-PC 汇编语言程序设计》的习题答案,其实就是把一个初学者最容易卡住的那些点,一个一个拆开揉碎了给你看:十进制怎么变成二进制和十六进制、负数在内存里用什么形式存在、CPU 算加法时凭什么知道结果对不对、内存地址怎么由段地址和偏移地址拼出来。这些问题如果你只是上课听讲,很容易觉得“好像懂了”,但一到做题就会露馅。而习题答案的价值,就是让你有一份对照答案,知道自己到底哪一步推错了、哪里理解歪了。一句话说透:汇编习题答案不是用来背的,它是你在“机器码世界观”里迷路时的导航地图。
2. 数制转换:为什么十六进制是汇编的“母语”
2.1 进制不是数学题,而是一种“分组习惯”
你第一次做进制转换,可能是在算 369 等于多少二进制。但如果只把它当成数学题,就错过了最重要的东西。日常十进制里我们“逢十进一”,二进制是“逢二进一”,十六进制是“逢十六进一”。听起来很绕,但打个比方就明白了:你数鸡蛋,既可以一个一个数(二进制),也可以两个两个数(还是二进制),还可以十六个装一盒、按盒数(十六进制)。盒子的存在不是为了难为你,而是因为一盒刚好对应一种常见的东西——比如 CPU 里的 8 位、16 位寄存器。
习题里反复出现的套路是:(1) 369 = 1 0111 0001B = 171H。拆开看,先转二进制,再把二进制从右往左每四位分一组,每组对应一个十六进制位。为什么每四位一组?因为四位二进制最大是 1111,正好等于十六进制的 F,不多不少。这就是十六进制存在的根本理由:它是二进制的“压缩写法”,一位十六进制等于四位二进制,不用像十进制那样做复杂的乘除法才能换算。
2.2 转换不是死算,而是看“位权”
很多人做转换慢,是因为在硬算。比如把 32767 转二进制,就不断除以 2、记余数,一直除到 0。这种方法没错,但效率低,而且容易错。更聪明的做法是直接看“位权”:二进制每一位代表 2 的 n 次方,十六进制每一位代表 16 的 n 次方。32767 这个数很特殊,它等于 2 的 15 次方减 1,所以二进制是 15 个 1,即 111 1111 1111 1111B,转十六进制就是 7FFFH。
习题答案里给的(3)4095 = FFFFH、(4)32767 = 7FFFH,其实全都在暗示这个规律:当你知道一个数接近 2 的整数次幂时,直接写二进制要比做除法快得多。这也是汇编实战里的血泪经验:写程序时经常要算 mask(掩码)、算边界值,能一眼看出“这个数是 2 的 n 次方减 1”的人,写出来的代码比拿着计算器的人快一倍。
2.3 反向转换:十六进制回二进制,考验的是“拆位”能力
从十六进制转二进制,是汇编里更常见的操作,因为你在调试程序时看到的寄存器值往往都是十六进制,比如 AX = 4C2AH,你要快速知道它的二进制长什么样。方法很简单:把每一位十六进制数展开成四位二进制。4 是 0100,C 是 1100,2 是 0010,A 是 1010,拼起来就是 0100 1100 0010 1010B。但这里有个初学者容易翻车的地方:C 的二进制是 1100,拆出来不足四位,要在前面补 0 补足四位。很多人直接写 C 对应的二进制 1100,结果后面拼起来位错位了,整个数就废了。
另一个练习是”拆位补位”题,比如把 FAH 转二进制,F 是 1111,A 是 1010,合起来是 1111 1010B,对应十进制 250。这里的 250 就是 255 减 5,是汇编里很常用的“上界数”。这类数字见多了,你对二进制和十六进制的直觉就建立起来了,不再需要每次去查表。
3. 补码与标志位:负数在计算机里的“分身术”
3.1 减法为什么能变成加法:补码的钟表模型
习题 1.5 里有一组很“劝退”的题:用 8 位二进制补码计算 (-85)+76、85-(-76) 这类式子,还要判断 CF 和 OF 标志。要理解这题,先得回答一个更基础的问题:为什么计算机不用“符号位加绝对值”这种直观表示负数,偏要用补码?打个比方,钟表上 9 点可以理解为 12 点往前拨 3 小时,也可以理解为往后拨 9 小时。在 12 小时的体系里,-3 和 +9 是同一个位置。计算机的补码就是这个道理:8 位二进制有 256 个状态,把 128 到 255 这些“看起来是正数”的二进制数重新解释成 -128 到 -1,就得到了补码。
补码最大的好处是:减法可以统一成加法。85-76 在机器里不是直接做减法,而是 85 加上 -76 的补码。习题答案里专门把 85-76 改写成了 85+(-76) 的补码相加形式,就是这个原因。CPU 里不需要单独造一套减法电路,加法和减法共用一套加法器,硬件更简单,速度更快。这就是补码存在的全部意义:用一套加法逻辑通吃正负数的加减运算。
3.2 标志位不是“辅助信息”,而是运算结果的“体检报告”
习题 1.5 的答案在每个算式后面都跟了 CF=0、OF=1 这样的标志,很多初学者直接忽略它们,觉得“我算对结果不就行了”。但标志位恰恰是汇编里最核心的隐藏信息。CF(进位标志)记录的是无符号运算时最高位有没有向更高位进位或借位;OF(溢出标志)记录的是带符号运算时结果有没有超出表示范围。两者看的都是“越界”,但一个按无符号解释,一个按带符号解释。
举个例子:85-(-76) 算出来是 0A1H,如果把它看作无符号数,0A1H 是 161,没问题;但如果你把它看作带符号数,0A1H 的最高位是 1,代表负数,这就不对了。答案给出的 OF=1 就是在说:带符号视角来看这个结果溢出了。而 CF=0 表示无符号视角没越界。同一段二进制,两种身份两套标准,这就是汇编初学者最懵的地方:一个数到底该按无符号还是有符号看,取决于你把它放进哪条指令里。
3.3 从补码题里学到的“读寄存器”习惯
这些补码习题还有一个隐藏收获:培养读寄存器的习惯。每一道题的答案都写了“MOV AX, 1234H;(AX)=1234H”这类注释,看似啰嗦,其实是汇编调试的基本功。你在 DEBUG 里看寄存器,看到的永远是十六进制,比如 AX=0F7H,你得立刻反应过来:这可能是 -9 的补码,也可能是 247,具体是哪个取决于程序把它当什么用。这种“一个值多个身份”的思维方式,是汇编独有的。学过汇编再看 C 语言的 unsigned 和 signed,你会觉得 C 真是体贴,因为类型帮你把身份定死了。
4. 寄存器与寻址方式:CPU 的“工作台”和“取货方式”
4.1 寄存器不是一排储物柜,而是一张有分工的工作台
习题 2.7 问“下列操作可以使用哪些寄存器”,这种题看起来是在考记忆,其实是在讲 CPU 的工作台面布局。你可以把寄存器想象成厨房里的操作台:AX 是“主力炒锅”,加减乘除都靠它;CX 是“计数器”,循环的时候专门用它数次数;DX 是“辅助锅”,乘法和除法结果太长时用来放多出来的部分;BX 则比较特殊,既能算数,又能当“指针”指向内存地址。每个寄存器都有自己的脾气,不是随便乱用的。
习题 2.9 的连线题里有一组非常容易错的概念:存储器和堆栈。存储器是一个大仓库,数据按地址存放,想取哪个取哪个;堆栈则是一个叠盘子器,只能从顶端放盘子、从顶端取盘子,后放进去的先拿出来。这两个概念一混,后面的 PUSH、POP、CALL、RET 全都会理解歪。习题 3.13 专门画了堆栈区示意图,让你看到 SP 指针怎么随着 PUSH 和 POP 上下移动。答案里 (SP)=00B0H,先 PUSH 8057H,SP 变成 00AEH,再 PUSH 0F79H,SP 变成 00ACH,最后 POP,SP 又回弹到 00AEH。这个过程不看图很难有直觉:堆栈地址是往下长的,越 PUSH 地址越小,所以“栈顶”其实是地址最小的那个单元。这一点搞反了,写栈操作代码必踩坑。
4.2 寻址方式:你的数据放在哪,用哪种“物流方式”去取
寻址方式是第三章的核心,也是很多初学者的噩梦。它看起来像是一堆语法规则:[BX]、[BX+0B2H]、[BX][SI]、VAL[BX][SI],其实本质只有一句话:CPU 要想办法找到一个操作数的地址,不同写法代表不同“找法”。打个比方,你在外卖 App 上填收货地址,可以写“店里自取”(立即数,数据就在指令里),可以写“我家柜子第三格”(寄存器,数据就在寄存器里),可以写“幸福路 100 号”(直接寻址,地址直接写在指令里),可以写“我朋友家,钥匙在他邻居那”(基址变址,地址由寄存器组合算出来)。
习题 3.1 给了 (BX)=637DH、(SI)=2A9BH、位移量 D=7237H,让你算各种寻址方式的有效地址,就是在模拟这类“找货”过程。其中最容易出错的是第 (7) 小题:相对基址变址寻址,算出来的 EA=(BX)+(SI)+D=1004FH,但 16 位寄存器存不下这么长的地址,最高位的进位 1 直接丢掉了,最后 EA=004FH。这个“进位丢失”很有教学意义:它不是算错了,而是 CPU 真的会这么做。所以当你看到汇编代码里的地址明显不对时,先别怀疑代码写错,很可能是溢出了。
4.3 MOV 与 LEA:一个是搬家,一个是记门牌号
习题 3.10 专门考了一组非常经典的对比:MOV AX, TABLE 和 LEA AX, TABLE。前者把 TABLE 单元里的内容取出来发给 AX,后者把 TABLE 单元的地址编号发给 AX。答案很清楚:如果 TABLE 在 0032H 单元,里面存着 1234H,那么 MOV 之后 AX 是 1234H,LEA 之后 AX 是 0032H。
这两个指令的差别可以类比成:MOV 是去朋友家把他家的电视机搬回来,LEA 是只记下他家门牌号。很多新手写程序时习惯用 MOV AX, OFFSET TABLE 来取地址,其实更地道的写法就是 LEA AX, TABLE。它们效果相同,但 LEA 更直观,而且在某些场景下还能“顺便做加法”,比如 LEA AX, [BX+SI+2] 可以在取地址的同时完成一次地址计算,一举两得。习题 3.11 就是这种综合题:先 MOV BX, OFFSET TABLE,再 ADD BX, ENTRY,最后 MOV AX, [BX],这个三步连招在真实程序里非常常见,本质就是数组按下标取元素。
5. 最容易踩坑的 6 组易混概念:看完不再懵
这一章集中处理初学汇编时最容易混的几组概念。每一组都是“看着像,其实根本不是一个东西”,不掰开讲清楚,后续写多少代码都会错。
易混 1:存储器地址 vs 存储器内容。这是新手的第一道坎。地址是门牌号,内容是住在里面的人。MOV AX, [1200H] 是把 1200H 这个门牌号对应的房间里的人请出来,而 MOV AX, 1200H 是把 1200H 这个数字本身当成数据。为什么容易混?因为汇编指令里数字太多,看着都差不多。怎么区分?看有没有方括号。方括号就是“去这个地址取内容”的意思,没方括号就是“这个数字本身”。
易混 2:物理地址 vs 逻辑地址。习题 2.4 是最经典的例子:3017:000A、3015:002A、3010:007A 三段不同的“段:偏移”组合,算出来的物理地址竟然都是 3017AH。原因很简单:段地址是左移四位再加偏移,3017H 左移一位是 30170H,加 0AH 得 3017AH;3015H 左移是 30150H,加 002AH 还是 3017AH。为什么容易混?因为平时我们用的一维地址就一个数,而实模式的内存地址要用两个数拼出来。怎么区分?物理地址是 CPU 真正放到地址总线上的值,逻辑地址是程序员写在代码里的“段:偏移”表示法。同一个物理地址可以有好几种逻辑写法,就像同一个房子可以说“幸福路 100 号”也可以说“幸福路 5 号院 2 栋 100 室”。
易混 3:CF 进位标志 vs OF 溢出标志。这两个标志都跟“数太大”有关,但服务的对象完全不同:CF 看的是无符号数有没有超过 0~255(8 位)或 0~65535(16 位)的范围,OF 看的是带符号数有没有超过 -128~127 或 -32768~32767 的范围。为什么容易混?因为一次运算可能两个标志同时变化,而且有时候 CF=1 的同时 OF=0,有时候反过来。怎么区分?记住一个判断口诀:如果最高位产生进位或借位就影响 CF,如果两个加数符号相同而结果符号变了就影响 OF。
易混 4:带符号数 vs 无符号数。同一个二进制数,两种解释两套结果。习题 1.6 里 D8H,按无符号数是 216,按带符号补码是 -40。为什么容易混?因为 CPU 不区分,它只管把二进制存起来,怎么解释全看指令和程序员。怎么区分?看后续指令:如果接着做无符号比较(JA、JB),就当无符号;如果做带符号比较(JG、JL),就当带符号。这是一切“看语境定身份”的源头。
易混 5:直接寻址 vs 立即寻址。MOV AX, [1200H] 和 MOV AX, 1200H,差一个中括号,意思天差地别。立即寻址是“把 1200H 这个常数塞进 AX”,直接寻址是“去内存 1200H 这个地址上把内容读进 AX”。为什么容易混?因为写起来只差括号。怎么区分?马上看有没有括号,没有括号的常数叫立即数,有括号的是地址。
易混 6:字节顺序 vs 高位在前。习题 2.2 里存放 1EE5H 在 000B0H 单元,答案是低字节 E5H 放在低地址 000B0H,高字节 1EH 放在高地址 000B1H,图里画得清清楚楚。这就是小端存储,Intel 处理器的默认习惯。为什么容易混?因为我们人类读十六进制时习惯从左往右看,大端在前,而小端存储是“低位在前”。怎么区分?记一个场景:当你用 DEBUG 的 d 命令看内存时,看到 34H 12H,对应的字是 1234H 而不是 3412H。只要这一条记住了,调试时就不会把数据读反。
6. 什么时候需要这份答案:啃汇编的三个关键时刻
这本《IBM-PC 汇编语言程序设计》的习题答案,最有用的场景不是考试前突击,而是三个具体的“认知关口”。
第一个关口是“数制刚打通,但还不敢做题”的时候。你刚学了进制转换和补码,觉得道理都懂,但一碰到 369 转二进制、85-(-76) 用补码算这种题,手就开始抖。这时候集中做第一、二章的习题,每做一题就对照一次答案,重点不是看最终数对不对,而是看你中间补的 0 够不够、补码转换的符号扩展有没有漏。我当年在这一阶段最大的毛病就是二进制转十六进制时补 0 不规范,明明 101 1011B 是 5BH,我当成 5B 少补了一位 0,结果后面全错。对照答案能很快揪出这类系统性错误。
第二个关口是“寻址方式学完,觉得全都会,做题全不对”的时候。第三章的寻址方式题是最值得反复做的,因为它不是死记语法,而是训练你在脑子里建立“寄存器内容 + 位移量 + 段默认规则 = 物理地址”的完整链路。习题 3.4 给了 (DS)=2000H、(BX)=0100H、(SI)=0002H 和一大串内存数,让你算七条指令执行后 AX 的值,这种题做一遍顶看十遍概念。我第一次做的时候,(5) MOV AX, 1100[BX] 和 (7) MOV AX, 1100[BX][SI] 被我完全搞混,后来对照答案才发现一个是基址加位移,一个是基址变址加位移,差一个 SI 就完全是另一个地址了。
第三个关口是“要写一个真正的小程序,但不知道从哪下手”的时候。第三章后半部分有大量字符串操作的习题,比如把字符串从右到左传送、查找字符串中的空格、把 ‘&’ 替换成空格,这些几乎是汇编实战里最常见的操作。习题 3.30 的答案里有一段 REPNE SCASB 之后用 DEC DI 修正指针的代码,这就是无数人翻过车的经典坑:REPNE SCASB 找到匹配字符后,DI 会停在匹配位置的下一个单元,必须 DEC 回去才能正确替换。答案里的注释把这个细节标得明明白白,你看一遍记一辈子。
从那以后,我每次查一个汇编概念都强制自己走一遍“先做题、再对答案、最后总结经验”的流程,而不是直接翻结论。这个过程能帮你把“看懂了”变成“真会了”。希望你也能用这份答案,把汇编这座看起来吓人的山头,一点一点翻过去。希望帮到你。
本文还有配套的精品资源,点击获取