1、二进制基础:计算机为什么用二进制、位与字节、十六进制表示法、二进制与十进制的转换
各位同学好,我是老周。在二进制文件的世界里摸爬滚打了十几年,今天咱们来聊聊最基础、也最容易被忽视的东西——二进制本身。
很多人觉得二进制就是一堆0和1,太简单了。嗯,确实简单。但恰恰是这种简单,造就了整个数字世界的基石。我见过不少工程师,写上层代码溜得很,一碰到二进制数据就抓瞎。说白了,就是基础没打牢。
1.1 计算机为什么用二进制?
这个问题我当年刚学计算机时也想过。为什么不用十进制?我们人类用十进制不是挺方便吗?
原因其实很朴素:物理实现上,二进制是最可靠的。
你想想看,一个开关只有两种状态:开或关。一个电压只有两种电平:高或低。一个磁畴只有两种方向:正向或反向。如果用十进制,你得区分10种不同的电压或状态——这在工程上几乎不可能做到稳定可靠。
核心原因有三点:
- 物理实现简单:两种状态容易区分,抗干扰能力强
- 运算规则简单:二进制加法表只有4条规则(0+0=0, 0+1=1, 1+0=1, 1+1=10),十进制加法表有100条
- 逻辑代数基础:布尔代数只有真/假两种值,天然适配二进制
我在做嵌入式系统时遇到过一件事:某款芯片在高温环境下,3.3V的电压会漂到3.0V。如果是十进制电路,3.0V算几?但二进制电路只要区分高于2.0V还是低于0.8V,3.0V稳稳地算高电平。这就是二进制的容错优势。
1.2 位与字节
先明确两个基本概念:
- 位(bit):二进制中的一位,取值0或1。这是计算机信息的最小单位。
- 字节(byte):8个bit组成1个byte。这是计算机存储和处理的基本单位。
为什么是8位?不是7位也不是9位?
历史原因占大头。早期IBM的System/360用了8位字节,后来Intel的8086也用了8位,这个标准就这么延续下来了。我个人习惯把字节想象成一个8格的盒子,每个格子里放一个0或1。
| 单位 | 缩写 | 大小 | 常见用途 |
|---|---|---|---|
| 位 | b | 1 bit | 标志位、开关状态 |
| 字节 | B | 8 bits | 字符、小整数 |
| 字 | word | 16 bits (2 bytes) | 早期CPU处理单位 |
| 双字 | dword | 32 bits (4 bytes) | 现代CPU处理单位 |
| 四字 | qword | 64 bits (8 bytes) | 64位系统指针大小 |
避坑指南:我曾经在解析一个网络协议时,把16位字段当成了2个字节来处理,结果忘了考虑字节序(大端/小端)。数据全乱了。记住:位是概念单位,字节是存储单位。读写二进制文件时,你操作的最小单位是字节,不是位。
1.3 十六进制表示法
二进制写起来太长了。一个32位的整数,写成二进制是32个0和1,谁看得下去?
十六进制就是二进制的"缩写"。4位二进制对应1位十六进制,简洁又直观。
| 二进制 | 十六进制 | 二进制 | 十六进制 |
|---|---|---|---|
| 0000 | 0 | 1000 | 8 |
| 0001 | 1 | 1001 | 9 |
| 0010 | 2 | 1010 | A |
| 0011 | 3 | 1011 | B |
| 0100 | 4 | 1100 | C |
| 0101 | 5 | 1101 | D |
| 0110 | 6 | 1110 | E |
| 0111 | 7 | 1111 | F |
我建议你把这16个对应关系背下来。真的,不骗你。做二进制分析时,看到0x4A能立刻反应出0100 1010,速度会快很多。
在代码里,十六进制通常用0x前缀表示:
// C语言示例
unsigned char data = 0x4A; // 二进制 0100 1010
unsigned int magic = 0x1A2B3C4D;
注意:十六进制只是表示法,不是数据本身。0x4A和十进制的74是同一个数,只是写法不同。很多新手会混淆"表示形式"和"实际值",调试时吃了不少亏。
1.4 二进制与十进制的转换
这部分是基本功。我直接上方法,不绕弯子。
二进制 → 十进制
方法:按权展开求和。每一位的值乘以2的幂次,然后加起来。
二进制: 1 0 1 1 0 1
权值: 2^5 2^4 2^3 2^2 2^1 2^0
32 16 8 4 2 1
计算: 1×32 + 0×16 + 1×8 + 1×4 + 0×2 + 1×1
= 32 + 0 + 8 + 4 + 0 + 1
= 45
十进制 → 二进制
方法:除2取余,逆序排列。
十进制: 45
45 ÷ 2 = 22 余 1 ← 最低位
22 ÷ 2 = 11 余 0
11 ÷ 2 = 5 余 1
5 ÷ 2 = 2 余 1
2 ÷ 2 = 1 余 0
1 ÷ 2 = 0 余 1 ← 最高位
逆序读取余数: 101101
我刚开始学的时候,总觉得除2取余太慢。后来发现一个技巧:记住2的幂次表,用减法来做更快。
2^0=1, 2^1=2, 2^2=4, 2^3=8, 2^4=16, 2^5=32, 2^6=64, 2^7=128
十进制 45:
45 - 32 = 13 → 第5位是1 (32)
13 - 8 = 5 → 第3位是1 (8)
5 - 4 = 1 → 第2位是1 (4)
1 - 1 = 0 → 第0位是1 (1)
结果: 101101 (第5、3、2、0位为1)
个人经验:做二进制文件分析时,我几乎不用手动转换。用计算器或者写个小脚本就行。但理解转换原理很重要——它能帮你建立"数值感"。看到0xFF知道是255,看到0x100知道是256,这种直觉在调试时非常有用。
知识体系总览
下面这张图把本章的核心知识点串起来了。你可以把它当作一个思维导图来看。
这张图把四个核心知识点串在了一起。你注意看,所有分支都指向同一个中心——二进制基础。后面的章节会在这个基础上不断叠加新的概念。
好了,第一章就到这里。内容不多,但都是硬通货。把这些基础打牢,后面解析ELF、PE、Mach-O这些二进制格式时,你才不会晕头转向。