c 语言里面有符号整数在内存中是以补码形式存储的,设计补码的核心目的,是让减法运算可以转换成加法运算,统一加减法的电路实现,不需要单独设计减法硬件,同时解决原码里面正负零两种表示的问题。正数的补码和原码完全一样,负数的补码计算规则是先写出该数字绝对值的原码,然后按位取反得到反码,最后反码末尾加 1 就得到补码,这个三步法是考试和入门最常用的计算方式。c 语言中 int char 这些有符号类型,最高位是符号位,符号位 0 代表正数,1 代表负数,剩下低位用来存数值部分,计算补码的时候必须固定位数,比如 char 是 8 位,int 是 32 位,位数不一样补码结果也不一样,这点很关键,很多人计算错误就是没有固定位宽。理解 c 语言中补码怎么算的之后,还要知道补码转回原码的方法,负数补码转回原码仍然是取反加 1,同时补码可以实现溢出回绕,比如 char 类型最大值再加 1 就变成最小值,这个现象本质就是补码运算的特性,学习指针 整数溢出 位运算的时候都会用到补码底层原理。
基础概念铺垫
原码 最高位符号位,后面是数值二进制,直观但是有正负零,减法不方便
反码 正数反码等于原码,负数反码是符号位不变,数值位全部取反
补码 正数补码等于原码,负数补码是反码加 1
符号位 固定位宽的最左边一位,0 正 1 负,参与补码运算,不是单纯标记
补码计算步骤
正数补码计算
不需要额外变换,原码就是补码
举例 8 位 char,数字 + 5
原码 00000101
补码 00000101
负数补码计算 三步法
第一步 写出这个负数绝对值的原码,固定总位数,符号位先写 0
第二步 符号位保持不变,数值位全部按位取反,得到反码
第三步 反码最低位加 1,进位正常传递,得到最终补码
举例 8 位 char,数字 - 5
绝对值 5 原码 00000101
数值位取反得到反码 11111010
反码加 1 得到补码 11111011
补码还原原码
如果是负数补码,还原原码仍然执行 取反加 1,符号位保留
举例补码 11111011
数值位取反 11111010
加 1 得到原码 10000101,也就是 - 5
特殊值 最小负数
固定位宽里面,最小负数没有对应的正数,比如 8 位 char 最小值是 - 128,补码是 10000000,这个特殊值不能用常规取反加 1 得到正数,因为 + 128 超出 8 位有符号范围,这个是补码重要考点。
补码运算特点
加减法统一成加法,减法 a 减 b 等价于 a 加上负 b 的补码
运算产生超出位宽的最高进位直接丢弃,也就是溢出回绕
没有正负零,+0 补码和 - 0 补码统一成 00000000,消除两种零的问题
8 位补码示例对照表
| 十进制 | 原码 | 补码 |
|---|---|---|
| +3 | 00000011 | 00000011 |
| -3 | 10000011 | 11111101 |
| +7 | 00000111 | 00000111 |
| -7 | 10000111 | 11111001 |
深度易错要点
第一点 计算补码必须固定位数,8 位 char 和 32 位 int 补码不一样,不能随便截断高位,高位要按照符号位扩展
第二点 符号位参与运算,不是单独标记,做加法的时候符号位也要一起计算,溢出进位直接舍弃
第三点 很多人误以为负数补码就是原码直接取反,忘记最后加 1,这是最高频错误
第四点 无符号整数没有补码概念,无符号数直接就是二进制原码存储,不要把补码规则套用到 unsigned 类型
第五点 整数溢出本质就是补码加法超出位宽,高位溢出丢弃之后数值回绕,c 标准里面有符号整数溢出属于未定义行为,但是底层硬件一般按照补码回绕执行
【常见问题】
问题 1 c 语言中补码怎么算的,正数需要取反加 1 吗
回答 1 c 语言中补码怎么算的,正数的补码和原码完全相同,不需要执行取反加 1,只有负数才需要对绝对值原码取反再加 1,这是 c 语言中补码怎么算的核心区分点。
问题 2 c 语言中补码怎么算的,为什么要固定位数
回答 2 c 语言中补码怎么算的,补码是绑定固定位宽的,char 是 8 位,int 是 32 位,位宽决定符号位位置和可表示数值范围,位数不同补码结果不一样,所以计算补码必须先确定总位数。
问题 3 c 语言中补码怎么算的,最小负数补码为什么特殊
回答 3 c 语言中补码怎么算的,像 8 位 char 最小值 - 128 补码是 10000000,这个数值没有对应的正数值,不能按照常规取反加 1 还原成正数,因为 + 128 已经超出 8 位有符号整数范围,属于补码特殊情况。
问题 4 c 语言中补码怎么算的,无符号 int 也要用补码吗
回答 4 c 语言中补码怎么算的,补码是针对有符号整数的存储方式,无符号 int 没有符号位,直接用二进制原码存储,不存在补码转换的计算流程,不能套用负数补码算法。
【免责申明】
本文讲解 c 语言中补码怎么算的,补码是计算机底层硬件存储规则,c 标准定义有符号整数溢出属于未定义行为,不同 CPU 架构位宽存在差异,示例以 8 位 char 和 32 位 int 通用模型讲解,代码和计算仅用于学习参考,本文不保证所有特殊编译优化场景表现完全一致。


