
你写过无数个int a -5;但你停下来想过没有——那个-5在内存里到底长什么样内存里只有 0 和 1没有负号这个东西。那负这件事是怎么被塞进 32 个比特里的直觉会告诉你这还不简单最高位放个符号位0 是正、1 是负剩下 31 位存绝对值。这就是原码。听起来天衣无缝可你真拿它做一次加法就露馅了。原码看着顺眼一算就崩用 4 位方便演示算一下1 (-1)1的原码是0001-1的原码是1001最高位符号位是 1直接相加0001 1001 1010最高位是 1表示负数值是-2。1 (-1) -2错得离谱。更尴尬的是原码里还有两个零0000是01000是-0。CPU 每次判断结果是不是零都得特判两次。加法器看到这玩意儿只想掀桌。反码离对近了一步但还差一点有人想负数的表示把符号位之外的所有位取反行不行这就是反码。-1的反码 1001取反符号位不动→1110再算1 (-1)0001 1110 1111。1111是-0反码的负零。结果是零了方向对了但那个恼人的-0还在。补码多走一步减法当场失业在反码基础上再加 1就是补码-1的补码 1110 0001 1111注意4 位里-1的补码是1111。再算1 (-1)0001 1111 100004 位存不下最高位的进位直接丢掉剩下0000。等于 0。成了。而且这次-0也消失了——补码里0000是唯一的零。为什么取反再加一这么神钟表早就告诉过你这背后的原理其实是一个你从小就会的概念模。想象一块 12 点的钟。现在是 12 点你想倒拨 5 小时到 7 点也可以正拨 7 小时同样到 7 点。因为钟的模是 12-5和7在模 12 下是同一个东西。计算机里的 n 位二进制模是2^n。所谓-x的补码本质就是2^n - x也就是把负数抬到模的另一个等价值上。那取反再加一是从哪冒出来的拆一下就清楚了2^n - x (2^n - 1 - x) 12^n - 1是一串全 1比如 4 位就是1111。全 1 减去 x等于把 x 的每一位取反。所以取反对应(2^n - 1 - x)再加 1补上那个1。就这么来的没有魔法。补码顺手办了另一件大事减法消失了CPU 里其实没有减法电路。那7 - 3是怎么算的把-3转成补码然后7 (-3 的补码)直接走加法器0111 (7) 1101 (-3 的补码) 10100 → 丢掉进位 → 0100 (4)一条加法电路同时干加法和减法的活。硬件省了一大块这是补码最硬核的价值——把别扭一次性留给人理解把简单永久留给机器执行。顺带为什么 8 位是 -128 ~ 127多出一个 -1288 位补码能表示的范围是-128 ~ 127负数比正数多一个。原因就在于那个唯一的零正数占掉0 ~ 127共 128 个剩下1000 0000这个最高位是 1 的数不再需要表示-0了就腾给-128。408 提示补码是数据的表示与运算里的核心选择题、计算题都爱考。重点抓三件事——补码的定义与转换、“取反加一”、补码的表示范围与溢出判断。一个反直觉的设计凭什么活了半个世纪补码看着别扭你要读一个负数得先取反加一才算得回它的值。但设计者清楚地知道这种别扭只发生在人看的时候而爽却发生在机器每一次执行加法的时候。用一次性的理解成本换一辈子的执行成本这笔账划算得不能再划算。关于数据表示还有一件更反直觉的事——浮点数加减法里0.75 0.125这种看着整整齐齐的数CPU 也不能直接加要先对阶再舍入0.75 0.125CPU 竟不能直接加浮点加减的对阶与舍入一次算给你看。补码和它其实是同一套逻辑机器的简单从来不是白来的。从 ENIAC 到今天你手里的手机CPU 的指令集换了一茬又一茬唯独负数用补码存这件事半个多世纪纹丝不动。真正好的设计不是你觉得顺眼而是几十年后回头看发现找不到一个更好的替代。想把这些底层原理串成体系为计算机考研打好底子推荐 B站【408实验室】的《数据结构》系统跟学。