C语言系列之——(4)原码、反码、补码
1.二进制和进制转换
1.1 什么是进制
我们常常听到 二进制、八进制、十进制、十六进制 这样的说法,这些是什么意思呢
其实,它们只是数值的不同表现形式
每种进制下,整数部分从右往左分别是个位、十位、百位…… 只是不同进制下每一位的权重不一样
不同进制下整数部分从右往左每一位的权重如下
- 二进制:20,21,22……
- 八进制:80,81,82……
- 十进制:100,101,102……
- 十六进制:160,161,162……
小数部分,小数点右侧,权重是进制的负幂次,从左往右依次:
- 二进制小数:2-1,2-2,2-3……
- 八进制小数:8-1,8-2,8-3……
- 十进制小数:10-1,10-2,10-3……
- 十六进制小数:16-1,16-2,16-3……
每一位上的数字在等于其进制数时会往下一位进一位,自己这一位变为0
(如10进制下每一位上的数最大只能是9,当某一位上数变为10时,会向其左边的第一位进1,自己变成0,所以9+1=10,79+1=80,78+30=108)
(十六进制下为表示每一位上大于9的数,用字母A代表10,B表示11,以次类推,一直到F表示15)
对应进制下表示的数字等于其每一位上的数字和对应权重之积的和
整数示例:15 在不同进制
- 二进制:11112 = 1×20 + 1×21 + 1×22 + 1×23 = 1 + 2 + 4 + 8 = 1510
- 八进制:178 = 7×80 + 1×81 = 1510
- 十进制:1510
- 十六进制:F16 = 15×160 = 1510
小数示例:二进制 0.1012 转十进制
0.1012 = 1×2-1+0×2-2+1×2-3=0.5+0+0.125=0.62510
1.2 进制间的转换
a.十进制数转非十进制数
整数部分:基除法(除基取余,逆序排列)
将整数部分除以基数 r,所得的商作为被除数继续除以基数 r,反复进行这个过程,直到商为 0;
保留每一步的余数,从最后一个余数读到第一个余数,最后一个余数为最高有效位,第一个余数为最低有效位,得到非十进制整数。
小数部分:基乘法(乘基取整,顺序排列)
对给定的小数乘以基数 r,所得乘积保留整数部分,小数部分再乘以基数 r,反复进行这个过程,直到乘积的小数部分为 0,或者达到要求的精度时停止;
保留每一步取出的整数,从第一个整数读到最后一个整数,从左到右排列,得到非十进制小数。
如果 “基乘” 法不能使小数部分收敛为 0,说明这个小数无法用该非十进制数精确表达,精度由保留的数字位数决定。
示例:十进制转二进制
整数部分:除2取余,逆序排列
8转二进制:
8 ÷ 2 = 4(余0)
4 ÷ 2 = 2(余0)
2 ÷ 2 = 1(余0)
1 ÷ 2 = 0(余1)
最后余数序列0001逆序排列为:1000
小数部分:乘 2 取整,顺序排列
十进制小数不断乘以 2,取出每次计算得到的整数部分;
剩下新的小数继续乘 2,直到小数部分等于 0(部分小数无法归零,表示该10进制数无法被2进制精确表示,按需保留精度)
取出的整数按顺序排列就是二进制小数
十进制 0.62510 转二进制:
(0.625 × 2 = 1.25) 取出整数:1,剩余小数 (0.25)
(0.25 × 2 = 0.5) 取出整数:0,剩余小数 (0.5)
(0.5 × 2 = 1.0) 取出整数:1,剩余小数 (0.0),结束
整数依次顺序:1 0 1 → 二进制小数 0.1012
b.2K进制间转换
-
对于 2K 进制数,每位都可转换为 K 位的二进制数
(如八进制的每个数字都可以用 3 位二进制数表示,0~7,000 ~111 ; 十六进制的每个数字都可以用 4 位二进制数表示,0~F,0000 ~ 1111) -
二进制数也可以转换为 2K 进制数,每 K 位组成一组,转换为对应的 2K 进制的一个数字
-
如果转换的两个数制的基都是 2 的幂次,可以先转换为二进制,然后再进行转换
示例:二进制转8进制
八进制下每一位的数字范围为0~7,刚好可以被三位二进制数覆盖(000 ~ 111)
规则:
- 整数部分:从二进制低位(最右边)开始,向左每 3 位分组;最左侧不足 3 位,高位补 0
- 小数部分:从小数点向右,每 3 位分组;最右侧不足 3 位,低位补 0
每组 3 位二进制换算成 1 位八进制
二进制 1011.112 转八进制
拆分:整数 1011 → 补0分组 001 011;小数 11 → 补0分组 110
001=1,011=3,110=6
结果:13.68
示例:十六进制转8进制
十六进制与八进制都是 2 的幂次进制,先把十六进制转为二进制,再将二进制转为八进制
十六进制 1 位对应 4 位二进制;八进制 1 位对应 3 位二进制
十六进制 2A.416 转八进制
- 十六进制每一位展开成 4 位二进制:
216=0010,A16=1010,416=0100
2A.416 = 0010 1010.01002- 整理二进制,按八进制规则,整数向左每 3 位分组,小数向右每 3 位分组,不足补 0:
整数部分:001010 → (001 010)
小数部分:0100 → (010 0),低位补 0 得到 (010 000)
完整分组:001 010 . 010 0002- 每组 3 位二进制转 1 位八进制:
001=1,010=2,010=2,000=0
结果:12.28
2.原码、反码、补码
2.1 前置知识准备
a.这三个码是什么
整型数据 / 整数的二进制表示方法有三种:原码,反码,补码

(char类型变量中存的实际是字符对应地ASCLL码值,所以也是整型类型)
b. 不同整数三码的区别
b.1 负数与非负数之间

总结下来就是:
非负整数的原、反、补码三码相同,只要得到其中一个码就得到了所有三码;负整数的三码各不相同,需要分别计算转换
b.2 signed和unsigned之间
b.2.1 计算机存储单位说明
在计算机中,最小的存储单位是比特(bit),1个二进制位=1bit
接下来比比特大的单位为字节(Byte),1字节=8bit
再之后的存储单位关系如下:
1KB = 1024Byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
(这里对存储单位进行说明主要是想补充:一个二进制位=1bit 这个前置知识)
b.2.2 signed和unsigned之间三码的区别
signed
1.有符号整数二进制的这三种表示方法下,二进制序列都有符号位和数值位之分
2.最高位的1位 (即所有比特位中最左边那位) 叫做符号位,其余位叫做数值位
3.有符号整数的正负数之分体现在符号位上,而数字的绝对值大小体现在数值位上
4.符号位上为0表示“正”(当然0是不是正数也不是负数),为1表示“负”
(下面以int为例,因为int的大小为4字节,所以其二进制表示下可以分配到32个比特位)

unsigned
二进制序列没有符号位与数值位之分,全是数值位

正是因为符号位和数值位的差别:
同类型的unsigned整数能够表示的最大值为signed整数可表示的最大值的两倍+1
unsigned只可表示非负数,signed可表示负数,0,正数
2.2 整数与三码间的转换
(因为非负整数三码相同,所以下面的原码->反码->补码只针对负整数而言)
a.整数->原码
直接将整数按照正负数的形式翻译成二进制得到的就是原码
int a = -10;
-15的原码为:(为方便观看,下面把32个比特位以一个字节为单位每8个间隔开)
10000000 00000000 00000000 00001010
b.原码->反码
原码符号位不变(如果有的话),其他位依次按位取反就可以得到反码
(按位取反规则:二进制每一位,0变1,1变0)
int a = -10;
-10的原码为:(为方便观看,下面把32个比特位以一个字节为单位每8个间隔开)
10000000 00000000 00000000 00001010
-10的反码为:
11111111 11111111 11111111 11110101
c.反码->补码
反码+1就得到补码(每一位逢2进1变0,若进位后总位数多出正确位数,则舍去多出的位数)
int a = -10;
-10的原码为:(为方便观看,下面把32个比特位以一个字节为单位每8个间隔开)
10000000 00000000 00000000 00001010
-10的反码为:
11111111 11111111 11111111 11110101
-10的补码为:
11111111 11111111 11111111 11110110
若反码为:
11111111 11111111 11111111 11111111
其转补码+1得1 00000000 00000000 00000000 00000000,比正确的32位多1位,则舍去多出来的那一位1,得补码:
00000000 00000000 00000000 00000000
d.补码->反码->原码
将上诉的三种转换倒过来执行即可
2.3 整数在内存中的存储与补码运算
a. 先上结论
计算机只认识二进制0和1,而整数实际是以补码的形式存储在内存中的,所以,所有的整数运算,都是用补码进行计算的
原码和反码只是人们用来计算补码的工具,内存中并不存在
b. 为什么一定要用补码存储整数
这是因为不用补码有两个硬伤:
1.原码和反码表示下,0都有两种表示形式
-0和+0都是同一个数0
但是:
(原码)
-0 -> 10000000 00000000 00000000 00000000
+0 -> 00000000 00000000 00000000 00000000
(反码)
-0 -> 11111111 11111111 11111111 11111111
+0 -> 00000000 00000000 00000000 00000000
这两种表现方式下,同一个数 0 都占了两个不同的编码,使得可表示的数少了一个,空间直接浪费了一个
而且还会导致运算逻辑混乱
而补码则能完美解决这个问题:
+0 = -0 -> 00000000 00000000 00000000 00000000
2.若不用补码存储整数,减法运算无法直接用加法实现,硬件成本高
计算机的 CPU 里,天生只有加法器,没有专门的减法器。如果用原码做减法,比如 5 - 3,就需要额外的电路来处理符号位、比较绝对值大小,再做减法,这会让硬件变得非常复杂,成本飙升。
而用补码存储整数进行减法运算,可以自动将减法运算变为加法运算,且结果正确
以 5 - 3 为例
补码下,5 - 3 -> 5的补码 + (-3的补码)
5的补码:00000000 00000000 00000000 00000101
-3的补码:11111111 11111111 11111111 11111101
5+(-3):1 00000000 00000000 00000000 00000010 ->舍去多的一位-> 00000000 00000000 00000000 00000010
00000000 00000000 00000000 00000010 恰好就是2的补码,5-3=2
c. 补码运算
上面已经说过,所有的整数运算,都是用补码进行计算的
那有哪些整数运算呢?
算数运算
常用的算术运算有 + , - , * , / , %
(因为除+-外其他都涉及一些位运算规则,所以这里先不进行讲解,等到讲操作符时就会将其他的这些运算补齐,并且说明进行补码运算时的 溢出 问题)
(在上面讲用补码存储的第二个原因的例子中已体现+-运算,所以不再赘述)
注意:一些不同类型的整型变量在进行算数运算时,会发生整型提升或算术转换,此时运算的过程就更复杂些,上面举的例子是同类型整型变量间的运算规则,至于整型提升与算术转换的场景,将在后续文章中解释~
(下面这些运算同样会在讲解操作符时补齐)
位运算:~,&,|,^,<<,>>
复合赋值运算:+=,-=,……
比较运算,+-号
posted on 2026-10-27 16:27 progressive_bird 阅读(7) 评论(0) 收藏 举报
浙公网安备 33010602011771号