算术与逻辑操作:CPU指令集与ALU数据通路的底层密码

发布时间:2026/10/9 5:17:27
算术与逻辑操作:CPU指令集与ALU数据通路的底层密码 把教材翻到“算术和逻辑操作”这一节很多人的第一反应是加减乘除、与或非这有什么好学的但等你真正去读汇编、调崩溃现场、看编译器生成的指令序列就会明白这一小节的含金量被严重低估了。尤其是一元操作、二元操作和移位操作这三块表面上是“指令清单”实际上藏着指令集设计、ALU 数据通路、标志位状态机、编译器优化等一系列底层逻辑。这篇笔记我按自己啃教材时的思路整理了一遍重点放在 3.5.2 和 3.5.3 两小节把“为什么这样设计”和“实际用起来要注意什么”都补齐了。适合正在学计算机组成原理、汇编语言的同学也适合工作后想补底层基础、或者写代码时想理解性能开销的开发者。1. 先把这一节的“坐标”找准算术与逻辑操作在指令集里的位置学习这一节最容易犯的错是把它当成一张孤立指令表来背。其实“3.5 算术和逻辑操作”是整个指令集设计的核心枢纽——无论 RISC 还是 CISC无论桌面处理器还是单片机CPU 能做的计算性工作最终都会落到这一节的这些操作上。后面的分支、函数调用、数据搬移全都依赖这些基础操作产生的值和状态。指令集的视角下操作分类有很多种切法按功能分算术、逻辑、访存、跳转按操作数个数分一元、二元、三元按寻址方式分。3.5.2 的“一元和二元操作”就是按操作数个数在切。之所以教材普遍这样分是因为操作数个数直接决定了指令格式一条指令要留多少位给操作数每条指令从哪取操作数硬件要准备几个输入端口全都和这个分类相关。比如 x86 里inc reg是一元操作只需要一个源add reg, reg是二元操作两个源一个目的而一些 RISC 风格里常见的addi是二元操作加一个立即数。这些差异在指令译码阶段就要被区分对待。理解“一元 vs 二元”本质上是理解一条指令在执行阶段需要什么样的数据通道路径。二元操作意味着 ALU 的两个输入端口都要用满数据通路上得有多路选择器去挑选寄存器值、立即数还是内存值一元操作意味着另一个输入端口往往被置成固定值比如 0或者直接被忽略。你去看任何一本教材里的数据通路图会发现 ALU 永远是两个输入一元操作并没有让 ALU 变成单输入它只是控制信号告诉 ALU “不需要右边的真实操作数”。这是个很经典的“复用硬件、不增加新功能单元”的设计思路贯穿整个计算机体系结构。1.1 指令编码里的空间博弈指令格式是有限的。x86 指令长则十几个字节短则一两个字节RISC-V 的常规指令固定 32 位还要分出 7 位 opcode、5 位目的寄存器、5 位源寄存器、5 位第二源寄存器剩下留给立即数和功能码的空间并不多。如果所有算术逻辑操作都设计成“任意三个寄存器 任意寻址方式”指令长度会爆炸译码器复杂度也会不可控。所以设计者做了一个折中高频操作给短编码低频操作给长编码。一元操作因为只用到一个源操作数编码空间可以更宽松甚至可以在某些指令格式里塞一个较大的立即数二元操作则要严格控制寻址方式组合像 RISC-V 的 R 型指令只支持“寄存器 寄存器”立即数运算是单独的 I 型指令去承载。明白了这个你再去看指令手册里的每条指令格式就会理解为什么这个指令是四字节、那个指令只占一字节。1.2 ALU 的真实形态两个输入端口如何兼容一元操作在数据通路上ALU 大多数情况下真的是“两个输入、一个输出、若干控制信号”。一元操作如取负、取反、自增、自减并不需要再拉一条单输入 ALU 的专用通路而是在控制信号里增加对应模式另一个输入端口通过多路选择器接上常数 0。x86 里neg eax的本质就是“0 - eax”inc eax的本质是“eax 1”第二输入固定为 1。not eax更典型——它甚至不需要走加法器控制信号让 ALU 直接按位取反即可。这就是我反复强调“要看数据通路图”的原因。你只背指令名称永远察觉不到一元操作其实是二元操作的特例化复用。硬件上少一套单元功耗和面积就少一分这条设计哲学以后还会在乘法器、除法器、桶形移位器上反复出现。2. 一元操作一个操作数也能玩出花2.1 典型一元操作与指令长相一元操作在常见 ISA指令集架构里主要就那么几个自增inc、自减dec、取负neg、按位取反not。有些架构还有abs绝对值、sign extend符号扩展等等但核心思想是一致的只读一个源操作数对它做变换把结果写回目的位置。常见 ISA 的一元操作里x86 的neg和not两种指令长得极其对称初学者第一次看到容易搞混not是逐位取反0变1、1变0没有任何“加一”动作neg是求二进制补码的相反数等价于“取反后加一”。别看只差一个“加一”它们的数学意义完全不同。也正因为一元操作只有“一个源 一个目的”指令里的另一个操作数字段常被复用成扩展位、立即数位或寻址模式位。比如 RISC-V 的 I 型算术指令里有 12 位立即数可以用来表达addi x1, x0, 5而x0是硬件上恒为 0 的寄存器所以这条指令某种意义上可以看作“向 x1 加载常数 5”——这是汇编器常用的小技巧本质上就是把二元操作绑定了一个常量源来模拟一元操作。2.2 负数和取反的位级真相neg这条指令虽然简单但它暴露了补码表示的精髓。一个数x的相反数在二进制补码下怎么算一次性背下来的结论是“取反加一”。为什么是取反加一因为一个数和它的相反数相加必须等于 0。假设x的补码是1111 ... 0001取反得到0000 ... 1110这时两数相加的结果是全 1再加一就能产生进位溢出最终为全 0。活生生的“取反加一”就是从“相加为 0”这个要求里反推出来的。很多人只背结论不推过程到了写位操作代码时就会犯糊涂想看一个数是不是 2 的幂有人直接x (x - 1)为什么这样能判断因为 2 的幂二进制里恰好只有一个 1。这和neg有什么关系关系在于x (-x)在补码下恰好提取出最低位的那个 1。如果你理解了“取负取反加一”-x的位模式就是把最低 1 左边全部取反右边保持全 0于是x (-x)就把最低位的 1 单独留了下来。这种细节不会直接考到你但写位运算和写哈希表时经常用得上。not指令的位级行为相对简单0 变 11 变 0。它与neg的关系也恰好是“差一个加一”neg x not x 1。在调试汇编时如果你发现一条neg的结果看起来像“取反但不对”请立刻想想是不是漏了加一。2.3 自增自减最容易踩前缀后缀坑的地方inc和dec是使用频率极高的一元操作因为在循环里几乎到处都是i、i--。但很多人在学到高级语言时有一个顽疾把“表达式里的前缀后缀”和“指令执行的自增自减”混为一谈。其实i和i的差异是语言层面的语义差异到了汇编级别对寄存器变量来说很可能就是同一条inc指令只是编译器会把旧值保存或延后读取以保证语言语义。当你亲手写汇编循环时inc loop_counter会让寄存器值直接加一同时影响标志位的状态。要注意的是inc和dec在 x86 上不会影响进位标志CF只会影响ZF、SF、OF和辅助标志AF。这个设计是为了凑出短指令编码也让编译器可以在不影响CF的前提下做循环计数。如果你习惯用add reg, 1替代inc reg两者在标志位行为上是有差异的——这在写底层代码时可能造成非常隐蔽的 bug。3. 二元操作加法器、与或异或与寻址方式二元操作的典型代表是add、sub、and、or、xor以及乘法mul/imul、除法div/idiv。相比一元操作二元操作真正把 ALU 的双输入端口用满了也是绝大多数程序里占比最高的一类运算指令。在“算术”和“逻辑”这对概念里教材会把加减乘除归为算术操作与或异或归为逻辑操作。这个归类非常自然因为前者运算的是“数”的数学意义后者运算是“位”的布尔关系。不过二者在实际硬件里是共用一个 ALU 的这个大单元的职责就是根据控制信号把两个输入组合成一个输出——是相加、相减、逐位与、逐位或全看控制端。3.1 加法与减法同一条加法器之路先问一个看起来愚蠢的问题减法需要单独的减法器吗答案是大多数架构不需要。二进制补码表示下x - y等价于x (-y)而-y又是not y 1。因此只要在控制信号里多一个信号来控制“是否对输入取反”和“是否在最低位加 1”同一个加法器就能既做加法又做减法。这就是为什么sub指令和add指令的延迟几乎一样也都是一个时钟周期内就能搞定。加法器本身的实现也有层次最简单的行波进位加法器从最低位开始逐级进位延迟随位数线性增长现代的 ALU 会用进位旁路加法器、超前进位加法器甚至更复杂的结构来优化关键路径。作为软件开发者你不需要手画每个加法器内部结构但要知道一条add指令的硬件成本不只是“一条线把两个数连进来”它的延迟直接影响 CPU 的主频上限。这里必须提一个每次都会有人问到的点无符号数和有符号数相加用的完全是同一套加法电路。0xFFFF当无符号数是 65535当有符号数是 -1但加法器不关心这些它只是机械地完成各位相加和进位传播。无符号溢出产生进位CF1有符号溢出产生OF1真正的“对错”由程序员和后续指令来判定。这个心智模型非常重要否则你很难理解为什么同样的二进制加法在不同类型下结果不同却都由同一条指令执行。3.2 乘除法为什么不能一条指令搞定和加减法不同乘法在硬件里不是“几个门电路就能完成”的。n 位与 n 位相乘中间要产生 n 个部分积然后累加如果全并行做面积爆炸如果做成逐位移位累加需要 n 个周期。指令集设计者对乘法一般有两种策略要么提供单周期/多周期乘法指令如 x86 的imul让硬件尽量加速要么像早期 RISC 风格那样先不提供乘法指令用移位和加法组合实现。理解了这一点你就明白为什么编译器看到代码里乘一个常量 6 时不会老老实实生成imul而是生成lea、移位、加法的组合x * 6 x * 4 x * 2 (x 2) (x 1)。乘法慢移位和加法快编译器会权衡指令条数和延迟。这种思维不仅是教材考点在实际性能敏感代码里也是常见优化手段。有符号乘法和无符号乘法在位级上也有差异两个有符号数相乘会产生符号扩展问题。x86 里区分了mul无符号乘和imul有符号乘如果你用错了乘法指令得到的结果可能完全不对。RISC-V 里则统一用mul但把低 n 位结果直接截断高位丢弃从而让无符号和有符号乘共享同一个低 n 位硬件电路需要高位结果时再使用另一条指令。这种“低 n 位乘法共用一个乘法器”的设计是教材里值得画圈的一个细节。3.3 与、或、异或的工程用途与或异或每条指令都能给出教科书定义但真实工程用途才是让你爱上它们的关键and最常用的是掩码操作。x 0xFF取出低 8 位x (1 n)判断第 n 位是否为 1。数据包头解析、位图操作全离不开它。or用来置位。x | (1 n)把第 n 位强制置 1对位标志位进行组合、拼装字节日志都很常见。xor用来翻转位。x ^ (1 n)把第 n 位取反更妙的是x ^ x恒等于 0所以很多寄存器清零场景用xor eax, eax而不是mov eax, 0——后者要额外占用一条指令编码前者只需要两个寄存器操作数且长度更短。能看懂汇编的人一眼就能看出这种“清零”写法的性能意图。很多教材会在这一节划重点逻辑运算都是按位独立的每个输出位只和对应输入位有关不存在进位传播。这也是为什么逻辑运算的硬件实现极其简单延迟比加法器低得多。做电路级优化时逻辑运算真就是“快得几乎不需要担心”。3.4 二元操作的操作数从哪里来寻址方式二元操作最复杂的地方不在 ALU而在“操作数到底放在哪里”。常见的寻址方式有这么几类寄存器寻址操作数在寄存器里最快。立即数寻址操作数编码在指令里。适合常数参与运算比如addi x1, x2, 100。存储器寻址操作数在内存里。需要先计算有效地址再发起访存延迟可能是寄存器的几十上百倍。寄存器间接寻址、基址变址寻址等用于数组、链表、栈等数据结构。你写高级语言的表达式a b * 2看似一条语句实际生成汇编时编译器要决定 a、b 是放寄存器还是临时存内存是加载一次还是多次使用同一地址。读汇编时如果看到add eax, [ebx4]这种指令要意识到这不是一次“纯算术”而是一次“读取内存并参与运算”的复合操作这也是 CISC 与 RISC 风格的关键差异之一。RISC 里读写内存必须有显式load/store指令算术操作只接受寄存器操作数CISC 里则允许算术指令直接带内存操作数。前者指令条数变多但硬件规则简单后者单条指令表达能力强但译码和管线化就复杂得多。4. 移位操作位粒度的搬运工3.5.3 的移位操作是很多人第一次接触“对位进行整体搬移”的概念。移位指令表面上是把位模式向左或向右移动 n 位但它比看起来复杂得多逻辑移位和算术移位是两回事循环移位和带进位循环又是两回事。移位不仅是一种数据变换手段还和乘法除法、位域操作、编码解码有千丝万缕的联系。4.1 五种移位操作一张表讲清楚不同 ISA 对移位的命名略有差异但核心操作就五种我按 x86 和通用概念对照整理如下操作x86 助记符行为关键用途逻辑左移shl/sal各位左移低位补 0最高位进入 CF无符号数乘以 2 的幂逻辑右移shr各位右移高位补 0最低位进入 CF无符号数除以 2 的幂算术左移sal与逻辑左移完全相同乘以 2 时符号位自然处理有符号数乘以 2 的幂算术右移sar各位右移高位补原符号位最低位进入 CF有符号数除以 2 的幂循环移位rol/ror移出的位从另一端补回像一个闭环数据精排、序列变换带进位循环rcl/rcrCF 加入循环链成为第 n1 位大整数移位、多字移位这张表里最容易让人困惑的是算术左移。因为补码表示下左移一位会使所有位向高位移动一位最低位补 0最高位进入 CF——无论这个数是正还是负乘以 2 的二进制效果都一样所以逻辑左移和算术左移在指令层面是同一回事。x86 里shl和sal的机器码完全相同只是助记符给人“语义提示”。算术右移则是另一个故事。一个负数如-2补码1111...1110右移一位希望得到-1补码1111...1111此时高位必须补符号位 1 才能保持数学意义上的除以 2。如果右移时高位补 0结果就会变成0111...1111一个巨大的正数完全破坏原有的负数语义。4.2 移位和乘除法的天然联系为什么说移位操作是算术逻辑里的“隐藏乘法器”关键在 2 进制的本质每向左移一位等价于乘以 2向右移一位等价于除以 2向下取整。二进制数从第 n 位的权重是 2 的 n 次方位模式被移动 k 位所有权重都放大或缩小了 2 的 k 次方倍。这条性质让编译器做常数乘法优化时深深依赖移位x * 8变成x 3x / 16无符号变成x 4。但要注意无符号数的右移才是真正的除以 2 的幂有符号数右移则必须使用算术右移才等价于向零取整除法。而且 C/C 里对有符号整数的右移行为是“由实现定义”的——大多数编译器在大多数平台上是算术右移但从标准语言角度讲编译器完全可以选择逻辑右移。所以跨平台移植代码时绝对不要写依赖有符号右移语义的代码否则换一个目标平台就可能出 bug。移位替代乘除法还有一个前提条件乘数/除数必须是 2 的幂。x * 7无法直接用一条移位完成但可以拆成(x 3) - x。编译器对这种“乘法常数分解”的搜索算法叫“乘数重写”本质上就是在加、减、移位三种基础操作之间找最近的一条。你在反汇编里看到的那些lea eax, [eax eax*4]就是在做这种等价变换本质就是“乘以 5”。4.3 循环移位到底有什么用循环移位是普通移位之外的另一大类很多人学到这里总觉得这只是数学游戏不知道实际有啥用。它在几个场景里确实价值巨大第一位图旋转和局部重排。比如一些图像编码格式里要把一行的位做整体旋转循环移位一条指令就能完成第二加密算法。密码学里的字节重排、密钥扩展、散列压缩中大量使用循环移位。现代哈希算法里的“循环右移 n 位”往往就是硬件层面的唯一指令开销第三带进位循环是构成多字宽大整数移位的基石。多字移位是大整数的经典操作。假设用一个 64 位的寄存器组去表示 512 位大整数要实现整个 512 位数左移一位不能简单对每个寄存器单独shl——单独的移位会丢失每个寄存器最高位的溢出位而这个溢出位正是相邻高档寄存器的最低位。正确的做法是把每个寄存器分成两步先循环左移一位把最高位转到 CF再用带进位循环左移把上一个寄存器的 CF 带入当前寄存器的最低位依次从低到高处理每个寄存器。这种技巧在实现大整数乘法、任精度整数、哈希算法时都是基本操作。4.4 硬件上怎么实现移位桶形移位器移位操作的硬件实现也值得提一笔因为它是“面积和延迟”权衡的经典案例。最简单的方式是逐级移位移 1 位需要一个多路选择器移 2 位需要再接一级移 4 位再接一级移 n 位就需要 log2 n 级串联——这种设计被称为桶形移位器它像水管一样每级可选“移位 2 的 k 次方或不移”最终任意 0 ~ n-1 位的移位都能在一个时钟周期内完成。相比之下如果设计成“移一位的重复执行单元”硬件面积可以大幅减小但移 31 位就要 31 个周期延迟太高。几乎所有现代处理器都采用桶形移位器用更多晶体管换一个周期的延迟。这也是为什么在指令集中任何移位操作都被设计成单周期指令而不是循环若干次——硬件已经把“几条移位是常数时间”这件事焊死了。不过当一个循环里真的出现依赖动态变量作为移位量的场景时如x n中的 n 是运行时变量x86 允许用cl低 8 位寄存器来存放移位量比如shl eax, cl。这里有个新手经常踩的坑x86 的硬件逻辑只使用cl寄存器的低 5 位作为移位量也就是说最多 31 位。如果你试图shl eax, 32实际结果不是 0而是由硬件实现定义的“不做移位”或按低 5 位循环回绕得到的结果完全不可预测。这就是为什么 C 语言标准明确规定“移位位数小于类型宽度是未定义行为”的原因之一——硬件就是这么实现的。4.5 编译器眼中的移位从代码到指令写代码时x 4和x * 16编译器生成的汇编往往完全一样因为 CPU 有单周期移位指令编译器自然把“乘以 2 的幂”重写成移位。反过来x 4无符号时会用逻辑右移shr有符号时用算术右移sar。这个区别直接体现在你printf一个负数右移结果时如果用的是无符号类型会得到“除以 16 的向下取整”结果而有符号类型则可能是另一个负数——全都源于shr和sar的高位补法不同。另一个常见的编译器行为是当你想提取一个位域时比如从一个状态字节里取第 3 到第 5 位编译器会生成“右移 与掩码”的组合指令。这一步骤的本质就是shr把目标位段移动到最低位再用and把高位清零。如果你能读懂这些移位指令将来阅读反汇编代码时就能把一串shr、and、or还原成高级语言的高层逻辑排查问题也会快得多。5. 标志位算术逻辑操作真正的“副产品”算术逻辑操作除了把结果写到目标寄存器还会产生一组“状态信息”这组信息被保存在标志位或者说条件码里。高级语言程序员几乎察觉不到它们但汇编开发者、编译器后端、调试器都要时刻关注。3.5 这一节如果只学会指令怎么用而没学会看标志位那等于只学了半截。5.1 四个关键标志位各自管什么不同架构里的标志位不太一样但四个基础标志位的含义基本共通标志x86 里名称含义ZFZero Flag若运算结果为 0则置 1SFSign Flag若结果的最高位为 1则置 1有符号负数标志CFCarry Flag无符号运算的进位/借位标志OFOverflow Flag有符号运算的溢出标志理解 ZF 和 SF 都很直观结果是否为 0、最高位是否为 1。CF 和 OF 则要结合有符号/无符号的视角分开看这也是最容易混淆的核心点。举个例子来区分假设 8 位运算0x80 0x80结果为0x00。如果把它看成无符号数实际是 128 128 256超出 8 位范围最高位产生的进位会被 CF 捕获因此 CF1结果截断为 0。如果把它看成有符号数实际是 -128 (-128) -256同样不在 8 位有符号范围内符号从负变正这就是“溢出”OF1。同一个加法结果CF 和 OF 同时置位但表达的意义完全不同。5.2 加法和减法是如何设置标志位的在加法器内部标志位的生成逻辑几乎是免费的最高位产生的进位直接影响 CF最高位向符号位进位与否的比较决定 OF各位置零结果映射 ZF最高位复制给 SF。减法标志位的生成则有点反直觉。sub x, y实际是x (-y)这个过程中“借位”概念被补码加法巧妙地转换成“进位”。当无符号意义上发生借位时CF 会被置 1等价于x y。所以指令集里有个叫做cmp的操作本质就是做一次减法只是不写回目的寄存器只更新标志位。有了 CF 和 ZF一条cmp就能提供小于、大于、等于的全部比较信息ZF1 表示相等CF1 表示低于无符号视角这就是后续条件跳转指令的输入基础。5.3 cmp 和 test不存结果只改状态在汇编里你经常会看到这样两行cmp eax, ebx jle some_labelcmp用 eax 减 ebx把结果丢弃但更新标志位jle再根据标志位跳转。如果你潜意识里把cmp当成普通减法就会困惑“结果去哪了”。其实它的重点就是标志位结果被故意丢弃了。同理test eax, eax是“用 eax 与 eax 做与运算丢弃结果只更新标志位”常用它来检查某个数是否为 0、是否为负。这种“计算但不保存结果只看状态”的指令在代码里其实比你想的多得多。5.4 溢出判断的两个实战例子判断无符号数溢出只要看 CF。比如做两个无符号数加法如果 CF1说明结果超过类型上限。这个逻辑在实现任意精度整数时是核心。判断有符号数溢出看 OF。最经典的例子是“两个正数相加结果为负”或“两个负数相加结果为正”。为什么能这么说因为符号正确的加减法不可能让结果符号翻转。检验 OF 的一种直观方法是如果两个操作数符号相同但结果的符号与它们不同说明必然溢出。在 C 语言里有符号整数溢出是未定义行为而很多程序员等到程序出现奇怪结果时才想起去查有没有溢出。真正规范的工程做法是在关键计算前先做范围判断或使用编译器提供的溢出检查机制。如果手写汇编则可以直接检查 OF 标志位写高级语言时则要自己预判操作数范围。6. 常见问题、坑点与学习建议这一节内容看着少实际学习时踩坑的机会非常多。我把自己和身边同学踩过的坑集中整理一下对你也许有直接帮助。6.1 分清逻辑右移和算术右移千万别用错最常见的 bug 是对负数做右移时用了逻辑右移导致结果变成一个巨大正数。比如用无符号类型接收一个有符号数然后右移你本来想得到“负数除以 2”实际却得到了“无符号大整数除以 2”。另一个常见场景是写位运算代码时希望保留符号位却用时编译器给出的行为不符合预期——C 标准没有明确规定有符号右移的行为不同架构、不同编译器可能给出不同结果。解决办法是凡涉及有符号数的右移且语义依赖符号扩展务必明确类型、明确断言或者用显式的位掩码操作来模拟。6.2 移位量超宽的隐患前面提到 x86 的shl eax, cl只使用低 5 位作为移位量这在写编译器或者做代码生成时特别容易掉坑。如果某个循环动态计算移位量编译器和程序员都不一定能保证移位量 32硬件就会按低 5 位绕回产生一个看似随机的 31 以内移位量或者在某些 CPU 上根本不执行移位。C/C 里对超过位宽的移位是未定义行为所以安全的写法是先对移位量取模或使用条件判断进行保护。真正生产级的代码里 32这个条件判断非常值得写。6.3 标志位在调试中的实际用法在调试器里看标志位是验证代码逻辑的一条捷径。以 gdb 为例输入info registers可以看到eflags寄存器的当前值其中ZF位代表上次算术逻辑操作结果是否为 0。如果一段代码怎么跑都不进入预期的分支不妨在cmp之后查看eflags确认 ZF/CF/OF 是不是和自己预想的一致。很多时候“比较指令的结果与代码逻辑不匹配”并不是代码错了而是你高估了某些操作对标志位的影响。例如前面提过inc/dec不影响 CF如果你在inc之后判断进位那只会得到前一条指令遗留的 CF而非本次自增的进位。6.4 学完这一节我建议你做的小实验纸上得来终觉浅。这一节最好的学习方法就是打开编译器或者模拟器边写边验证。我建议你做一个 16 位或 32 位的二进制计算 Demo在命令行里输入表达式然后用位运算、移位操作去模拟加减乘除最后把每一步结果显示成二进制。这个过程中可以把移位实现乘法、逻辑右移和算术右移的区别、标志位怎么变化全部验证一遍。另一个行之有效的实验是写一段 C 代码编译成汇编看看常量乘法怎么被改写负数右移生成的是sar还是shr。你会从此对“编译器优化”四个字有远超同学的理解。最后分享一点我个人的体会。这一节之所以在整个教材里重要不是因为指令多而是因为它把所有“计算”的最终落点集中到了几个基础单元上让人看清了软件与硬件之间的那条转换链。学的时候一定要带着“这条指令在硬件里到底怎么动”的问题去读不要只记 mnemonics。等你以后真的翻开数据通路图、开始看流水线冲突时会发现这节的每一句话都在那里等着你。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询