2026/10/26 19:06:35

计算机系统概论第一章习题答案与自检:进制补码逻辑门机器模型

计算机系统概论第一章习题答案与自检:进制补码逻辑门机器模型 简介这份计算机系统概论原书第二版第一章习题答案面向正在学习计算机系统原理的高校学生、考研复习者及自学者用于对照课后练习核查概念理解与解题思路。内容围绕计算机通用性、模拟与数字系统差异、组合逻辑电路设计、性能优化、编程语言抽象层次及ISA等基础知识点展开并对第一章多道习题给出英文参考解答便于读者在阅读教材后复盘逻辑门运算、加法器与乘法器组合、出租车类比等问题。资源包共1个PDF文件约33KB篇幅精炼适合随教材同步查阅或考前快速回顾。目前已有721人学习下载可作为第一章知识框架梳理和习题订正的辅助材料帮助读者定位薄弱环节并加深对计算机系统基础概念的理解。1. 第一章习题为什么总在「看起来会、一算就错」上翻车翻开《计算机系统概论原书第二版》第一章的习题很多人会经历同一个过程正文读得挺顺合上书动笔进制转换少写一位、真值表漏掉一行、门电路的上拉下拉写反、机器模型题的周期数多算一拍。第一章的题目看着零散其实都在考同一件事——你能否把「电压高低」这一层物理事实一路推到「指令执行」这一层抽象中间不掉链子。所以这一章的习题答案价值不在于背下几个结果而在于你能不能自己产出答案、并且有办法验证它。下面按题型地图、位运算与补码、逻辑门与真值表、翻车排查、自检流程五段来拆每一步都给可复现的手算路径和能跑的对拍脚本。2. 第一章题型地图位、门、机器模型三条主线怎么分第一章的习题量通常不小但真正独立的题型不超过六类。先分类再动手能省掉一半返工。分类的标准不是「题目长什么样」而是「给你的输入是什么、要你交的输出是什么」——输入输出的形态一旦确定解法基本就锁死了。2.1 按输入输出形态给习题归类题型题目给你的输入你要交的输出主要工具高频错点进制与编码一个数 位宽另一种进制的定宽表示除基取余、乘基取整、四位一组位宽不固定、前导零丢失补码与运算两个定宽二进制串 运算结果串 溢出标志模 2^n 运算、符号扩展用进位当溢出门电路识图一张 CMOS 电路图布尔表达式或真值表串并联映射、对偶网络上拉网络与下拉网络搞反真值表反推一张真值表最小与或式卡诺图、代数化简输入排列顺序与表格不一致机器模型一段指令集描述微操作序列、周期数取指—译码—执行状态表周期重复计数、PC 自增时机错概念问答一段描述简答层次结构、抽象答成背书不落到具体位宽这张表可以直接当答题前的自检清单用拿到一道题先在上面找到它属于哪一行再去看对应那一列的「主要工具」。如果一道题你找不到归属大概率是把两个题型混在一起了比如「给一个十进制小数求它的单精度浮点表示」——它同时踩了进制转换和位宽定长两行必须拆开做。2.2 三条主线的推进方向不一样位这条线要自底向上先定位宽再定符号再算数值最后验范围。顺序颠倒就会出现「算完了发现位数不够」这种低级返工。举个最常见的场景题目给一个 8 位补码数要求做减法。正确的顺序是先确认两个操作数都在 8 位内、把减数取补、按模 2^8 相加、最后看两个操作数符号是否相同且与结果符号相反来判溢出。如果你上来就先算十进制真值再转回去中间任何一步的位宽都会成为问题。门这条线要双向对撞从电路图往表达式推一遍再从真值表往表达式推一遍两边对上了才算过。只走一个方向化简错一个圈你就发现不了。机器模型这条线要自顶向下先判断这条指令属于哪一类寄存器—寄存器、访存、跳转再决定它需要几个周期最后才往每个周期里填微操作。反过来做——先写微操作再数周期——几乎必然多算或漏算。三遍法在这里很好用第一遍只写格式位宽、变量个数、周期数不写数值第二遍填内容第三遍换一种方法验。第一遍花的三十秒能省掉后面十分钟的擦改。2.3 机器模型题的答题骨架第一章里最容易写成散文的就是机器模型题。规避办法是先搭一张固定格式的状态表再往里填。以一个虚构的 8 位教学机为例字长 8 位、地址 4 位、8 个通用寄存器指令格式是「4 位操作码 2 位目的寄存器 2 位源寄存器」。取指—译码—执行三段的骨架如下。节拍阶段微操作说明T0取指MAR ← PC把地址送上地址总线T1取指MDR ← M[MAR]PC ← PC 1读存储器同时 PC 自增T2取指IR ← MDR指令进指令寄存器T3译码按 IR[7:4] 选通路只做选择不改数据T4执行ALU ← RsALU ← ALU op Rt源操作数进 ALUT5写回Rd ← ALU结果落回寄存器填这张表有三条硬规则。第一一拍只做一件事同一拍里出现两个写操作基本可以判定写错了。第二PC 自增只出现在取指阶段而且只出现一次很多答案在跳转指令里又加一次自增导致目标地址偏 1。第三写回永远在最后一拍中间任何一拍写寄存器都会让后面的微操作读到脏数据。周期数就用表的行数直接数。如果题目问「执行 N 条指令共需多少周期」先分类全是寄存器型指令就是 6N中间夹一条访存指令访存那一条要额外加读数据、写数据两拍变成 8 拍。这种分类计数比笼统背一个平均数可靠得多也不容易被题目的措辞带偏。表填完最后做一次反向核对拿 T5 写回的结果倒推 T4 的 ALU 输入对不对再倒推 T3 的译码是否选对了通路。倒推一遍通常能立刻发现操作码字段切错了位——这是位宽题和机器题交叉地带的经典翻车点4 位操作码写成 3 位后面所有寄存器编号都会整体错位。3. 进制、补码与位宽手算流程加脚本对拍这一节的题量最大也最不应该丢分。手算流程要固化成肌肉记忆剩下的交给脚本对拍——把答案敲进 Python 跑一遍比反复检查十遍靠谱。3.1 四种进制互转的手算路径与检查点二进制和十六进制之间的转换是唯一不需要计算的四位一组从右往左切左端不足四位补零。八进制同理三位一组。这两条之所以要先记住是因为第一章里绝大多数进制题都能先转成二进制再做别的。十进制转二进制整数部分除二取余、倒序读小数部分乘二取整、顺序读。这里有个检查点特别有效整数部分每除一次商必须严格变小如果某一步商没变说明你抄错了。小数部分则要盯住题目给的精度比如「保留 6 位小数」第六位之后就该停不能靠感觉凑。二进制转十进制短串按权展开长串先切成四位的十六进制再展开出错率低一个数量级。比如一个 12 位串硬按 2 的幂加眼睛一花就漏了 2^7切成三组十六进制每组内部是 8-4-2-1几乎不会错。检查点的通用写法是三个「对不对」位数对不对题目要求 8 位你交了 7 位就是错、最高位对不对负数的最高位必须是 1、边界对不对结果是否落在该位宽能表示的范围内。这三个问题问完百分之八十的低级错误会被拦下来。3.2 补码、符号扩展位宽是唯一的锚点补码的定义只有一句话n 位补码的表示范围是 −2^(n−1) 到 2^(n−1)−1负数用 2^n 加上它的真值来表示。所有关于补码的题都可以还原成这一句。取负的标准操作是全部位取反再加一。翻车最多的是顺序颠倒成「先减一再取反」——对大多数数结果居然也对唯独在最小负数上出错属于典型的玄学错误平时测不出来考试专挑它。另外要记住 −2^(n−1) 取负还是它自己这是补码唯一的不对称点。符号扩展补的是符号位不是零。8 位的1000_1011扩展成 16 位是1111_1111_1000_1011值仍然是 −117。零扩展只适用于无符号数一旦混用数值会凭空多出一大截而且错得很隐蔽——因为位模式看起来「挺合理」。溢出判断也和进位无关。正确判据是两个同号数相加结果符号与操作数相反就是溢出。0111_1111 0000_0001 1000_0000进位是 0但它实实在在溢出了。用进位判溢出是第一章里最经典的一处血泪经验。3.3 用 Python 写一组对拍函数手算完想验最省事的办法是把「定义」写成函数然后批量跑性质检查。下面四个函数覆盖了第一章位运算题的绝大部分需求。def to_bin(n: int, width: int) - str: 整数 - 定宽补码二进制串。负数先映射到 2**width 的无符号空间。 mask (1 width) - 1 return format(n mask, f0{width}b) def twos_value(bits: str) - int: 补码串 - 有符号真值。最高位是 1 就减去 2**n。 n int(bits, 2) return n - (1 len(bits)) if bits[0] 1 else n def to_hex(bits: str) - str: 二进制串 - 十六进制左端补零到 4 的倍数后四位一组。 bits 0 * ((-len(bits)) % 4) bits digits 0123456789ABCDEF return .join(digits[int(bits[i:i 4], 2)] for i in range(0, len(bits), 4)) def sign_extend(bits: str, width: int) - str: 符号扩展左边补的是 bits[0]不是 0。 assert width len(bits), 只能往宽了扩 return bits[0] * (width - len(bits)) bits四个函数的参数含义要盯住两点。width必须显式传入绝不能靠len(bin(n))去猜——Python 的bin()会把负数输出成-0b101这种带符号的形式前导零也一个不留直接拿它当答案交上去必错。sign_extend里的断言不是装饰目标位宽比源位宽还小的时候这个操作根本没有定义早报错比晚出错强。函数有了接下来跑性质检查。性质检查的好处是不需要标准答案靠恒等式自己验自己。import random random.seed(7) for _ in range(200): w random.choice([4, 8, 12, 16]) s format(random.getrandbits(w), f0{w}b) v twos_value(s) # 性质1取负两次回到自身含最小负数 1000...0 的回绕 assert twos_value(to_bin(-v, w)) v # 性质2符号扩展不改变真值 assert twos_value(sign_extend(s, w 8)) v # 性质3十六进制往返不丢位 assert int(to_hex(s), 16) int(s, 2) print(200 组性质检查全部通过)这三条性质分别对应三类题。性质 1 管「求相反数」和「补码回绕」最小负数那一组会自然被覆盖到不用特意构造。性质 2 管符号扩展扩完再转回真值比对比肉眼数位靠谱。性质 3 管进制转换的位数对齐to_hex里那个补零到四的倍数的动作一旦漏写int(..., 16)立刻会给出不同的值。把题目里的具体数字替换进s和w就能对单题对拍把范围放大到 200 组就是一次自测。整个脚本不到三十行写一次能用一学期。4. 逻辑门与真值表从 MOS 管串并联到布尔表达式门电路题的失分点几乎全在同一个地方把上拉网络和下拉网络当成一回事。这两张网络在结构上是对偶的串联变成并联、并联变成串联输入条件还要取反。理解这一点识图题就不用背。4.1 串并联到与或的三条映射规则静态 CMOS 门有一个固定的解剖结构上面是拉向电源的 PMOS 网络下面是拉向地的 NMOS 网络输出从中间取。NMOS 高电平导通PMOS 低电平导通。第一条规则下拉网络决定输出为 0 的条件。下拉网络导通输出被拉到地就是 0。所以下拉网络的结构直接对应反函数 F′——串联对应「与」并联对应「或」这里的「与或」是导通条件的组合方式。第二条规则上拉网络与下拉网络对偶。看到上拉是「A 并 B」下拉就一定是「A 串 B」中间每一个管子的类型也互换。识图题只要把下拉网络画出来上拉网络基本可以照着推。第三条规则PMOS 的输入要取反。上拉网络里出现输入 A实际导通条件是 A 0写进表达式时要写成 A′。这一步漏掉得到的表达式会和真值表差一个反相而且往往只在部分输入组合上体现。举个三输入的例子下拉网络是「A 串B 并 C」那么 F′ A·(B C)输出 F A·(B C) 的反即 F A′ B′·C′。检查办法很直接——把 A1、B0、C0 代进去下拉导通输出应为 0而表达式给出 A′ B′C′ 0 1 1对不上说明哪里反了。这种「代一组数进去看输出该是 0 还是 1」的动作值得在每道门电路题上花十秒。4.2 真值表反向构造与卡诺图化简的手算步骤给真值表求最小与或式手算流程固定成五步一步都别跳。第一步列全 2^n 行。三变量就是 8 行四变量 16 行。行数不够是最常见的错误来源而且往往漏的就是让表达式化简不掉的那一行。第二步按格雷序排列输入。00、01、11、10不是 00、01、10、11。卡诺图的相邻格必须只有一个变量不同顺序错了圈出来的项就是错的。第三步填格、圈 1。圈的大小必须是 2 的幂1、2、4、8圈要尽量大圈可以重叠四条边和四个角是相邻的。这几个规则里「尽量大」和「可重叠」最容易被忽略导致写出一个正确但不是最小的表达式。第四步每个圈写一个积项圈内取值不变的变量保留变化的变量消掉取值为 0 的变量写成反变量。第五步全体圈求或然后用 4.3 的脚本验一遍。给一个具体的小例子。三变量 F(A,B,C)取 1 的最小项是 m1、m3、m5、m7。填进卡诺图这四个格正好是 C1 的那一列圈成一个 4 格的大圈A 和 B 都在变只有 C 不变结果是 F C。如果不圈大圈硬拆成四个 1 格圈会得到 A′B′C A′BC AB′C ABC虽然等价但远不是最小式。这就是「圈要尽量大」的实际价值。四变量的时候再补一条经验先圈孤立的 1再圈成对的最后圈大的。反过来做大圈会把小圈需要的格吃掉后面就得重叠最终项数反而多。4.3 用 itertools 枚举验证等价化简完最怕的是「看起来更短其实不等价」。用穷举把所有输入跑一遍两秒钟出结果。from itertools import product def truth_table(expr: str, names: list) - list: 按 00..0 - 11..1 的固定顺序枚举和手写真值表的顺序一致。 rows [] for values in product([0, 1], repeatlen(names)): env dict(zip(names, values)) # 禁用内置函数避免表达式里混进意外调用 rows.append((values, int(bool(eval(expr, {__builtins__: {}}, env))))) return rows def equivalent(e1: str, e2: str, n: int) - bool: names [fx{i} for i in range(n)] return truth_table(e1, names) truth_table(e2, names) # 原式四个最小项展开与化简式比对 print(equivalent((not a and not b and c) or (not a and b and c) or (a and not b and c) or (a and b and c), c, 3))参数上有三处要留意。names的个数决定了枚举的总行数 2^n变量名必须和表达式里的标识符完全一致大小写也算。eval的第二个参数把__builtins__清空是为了让表达式只能做布尔运算不会意外调到别的函数——这一步在验证别人的表达式时尤其重要。product([0, 1], repeatn)生成的顺序天然是 00..0 到 11..1和手写真值表从上往下的顺序一致所以两张表可以直接逐行比对不用重排。表达式里用and、or、not而不是、|、~。后者是按位运算~0得到 −1 而不是 1在布尔场景下会给出反直觉的结果这类错误在小脚本里非常隐蔽。真跑通之后还可以再加一条把真值表直接打印出来和手写的那张并排放。哪一行对不上一眼就能定位到具体输入组合比只看一个 True/False 强得多。5. 第一章答案核对时的五处高频翻车答案写完不等于写对。下面五条是按出错频率排的每条都给出可操作的定位方法遇到对不上的时候按顺序排查通常三五分钟能收敛。第一条进制转换结果看起来对位数少一位。现象是转换完的串比题目要求的位宽短前面缺零交上去直接被判格式错。原因是手算时习惯性地把前导零省掉或者用计算器工具时工具默认不补零。解决办法是转换前先把目标位宽写在草稿纸最上面写完立刻数位数不够就在左边补零。用 3.3 的to_bin时把width显式传进去函数会自动format补零不要图省事用bin()。第二条补码取负在最小负数上出错。现象是所有普通数都对唯独1000_0000这类数取负后得到1000_0000看着像没算其实是对的你却以为错了又改一遍改成0000_0000。原因是不清楚补码的不对称性n 位补码能表示的最小负数是 −2^(n−1)它的相反数 2^(n−1) 超出了表示范围按模运算回绕回自己。解决办法是记住这条边界并且在草稿上单独标出来。对拍脚本里的性质 1 会自动覆盖到它跑一遍就能确认。第三条真值表行数不够或者顺序不对。现象是化简出来的表达式代进某一组输入结果不对回头查真值表发现那一行压根没写。原因通常是变量个数增加后没重新数行数或者输入按 00、01、10、11 排卡诺图却按格雷序填两套顺序混在一起。解决办法是列真值表时先写输入列再写输出列输入列一律用格雷序写完数一遍行数是否为 2^n。三变量八行、四变量十六行数一遍只要三秒。第四条门电路题把上拉下拉写反或者 PMOS 忘了取反。现象是表达式和真值表在前几行对得上后面开始整段反相。原因是直接照着电路图从上往下读把上拉网络当成了输出为 1 的条件或者把 PMOS 的输入当正逻辑处理。解决办法是固定从下拉网络入手先写下拉网络的串并联结构写出 F′再取反得 F最后代一组输入验证。上拉网络只用来交叉验证对偶关系不作为主推路径。第五条机器模型题的周期数重复计数或 PC 自增位置错。现象是「执行 N 条指令需要多少周期」算出来比预期多一拍或几拍跳转指令的目标地址总是偏 1。原因是把 PC 自增同时算进了取指和执行两个阶段或者把译码单独算成一个完整周期却又在取指里重复算了一次。解决办法是照 2.3 的状态表填每拍只写一个动作PC 自增只出现一次。周期数就用表的行数数别凭印象估。这五条有个共同点都不是不会做而是流程里少了一个检查动作。把检查动作固定下来比多做十道题有用。6. 用一张自检表把第一章的答案钉死做完题之后我习惯给每道题补一行「可执行的一句话」——不是写答案而是写「这道题我凭什么认为它对」。这句话必须满足两个条件能手算复现能被脚本验证。做不到的说明这道题我其实没吃透只是碰巧蒙对了。具体做法是给每类题配一个固定的收尾动作整理成表就是这样。题型收尾动作判据进制转换数位数、看最高位位数等于题目位宽负数最高位为 1补码运算判溢出、查最小负数同号相加得异号即溢出符号扩展扩完转回真值比对扩展前后真值相等门电路代一组输入表达式与电路输出方向一致真值表化简穷举比对原式与化简式在 2^n 组输入上全等机器模型倒推微操作从写回结果倒推每一拍都能对上这张表最实用的地方在于它把「检查」变成了一个有终点的动作。很多人做完题不检查不是懒是不知道从哪儿查起——查什么、查到什么程度算完没有定义。表里每一行的判据都是可判定的做完就是做完不用反复纠结。再说一个具体技巧把化简题反过来做一遍。拿到最小与或式先用脚本枚举出它的真值表再拿这张真值表去画卡诺图看能不能圈回你原来那个式子。如果能说明化简过程可逆答案基本可靠如果不能多半是某个圈圈错了位置。这个方法对四变量以上的题特别有效因为人眼在 16 格卡诺图上找相邻关系很容易出错反过来验一次相当于换了个视角看同一张图。还有一个习惯值得养成草稿纸上永远保留中间态。位宽写在最上面真值表的输入列用格雷序机器模型的状态表按拍编号。这些中间态在检查时就是你的后悔药——答案对不上回头看一眼中间态就知道错在哪一步而不是从头再算一遍。我自己在这一章上翻过的车几乎都发生在「觉得太简单所以跳过检查」的时候。最典型的一次是四变量卡诺图圈完觉得挺顺直接交了结果边角那两格相邻的 1 没圈进去表达式少了一项代进那两组输入全错。从那以后我就养成了一个很笨的习惯任何一张卡诺图圈完必须用脚本穷举比对一次跑完再往下走。三十秒的事能省掉一次重做。希望帮到你。本文还有配套的精品资源点击获取