1. 计算机为什么只认二进制
这个问题要从物理层面说起。
电子电路最稳定、最经济的状态只有两种——通/断、高电平/低电平。用 0 和 1 两个数字刚好映射这两种状态,抗干扰能力强,制造成本低。
如果用十进制(10 种状态),电路要区分 10 个电压级别,噪声容限极小,稍有干扰就出错。所以二进制是硬件的最优解,不是人为选择的。
2. 为什么还需要十六进制和八进制
核心问题很简单:二进制太长,人读不了。
看一个对比:
| 十进制 | 二进制 | 八进制 | 十六进制 |
|---|---|---|---|
| 255 | 11111111 | 377 | FF |
| 4096 | 1000000000000 | 10000 | 1000 |
十六进制每 4 位二进制 = 1 位,FF = 11111111,压缩比 4:1。
八进制每 3 位二进制 = 1 位,Unix 文件权限 chmod 755 就是八进制表示。
本质:十六进制和八进制是人类可读的二进制缩写,不是计算机需要它们,是人需要它们。
3. 进制只在人类阅读的那一瞬间存在
这一点很重要——计算机内部存储的永远是二进制,进制只是显示格式:
你写 0xFF
↓
编译器/解释器把它翻译成 11111111
↓
CPU 永远只看到 11111111
↓
调试器为了让你能读,又显示回 0xFF
所以十六进制和八进制不是数据格式,是显示格式——和你给文件起中文名方便找是一个道理,本质是人类的拐杖,计算机从来不碰它。
4. 什么时候会碰到十六进制
你不需要主动去找内存里的数据,但当程序报错、调试、配置的时候,它会把内部数据打印出来给你看:
| 场景 | 你会看到什么 |
|---|---|
| 程序崩溃 | Segmentation fault at 0x7FFF5A3B |
| 颜色代码 | #FF5733 |
| 调试器断点 | 内存地址全是十六进制 |
| 网络抓包 | MAC 地址 AA:BB:CC:DD:EE:FF |
| Unicode 字符 | 汉字"你" = U+4F60 |
| 文件权限 | chmod 755(八进制) |
| 加密哈希 | SHA-256 结果一长串十六进制 |
这时候如果显示的是:
11111111111111111111111111111111(32 位二进制)4294967295(十进制,看不出位模式)0xFFFFFFFF(十六进制,一眼看出全是 1,32 位满)
哪个最快看懂?显然是第三个。
5. 地址不只是给你"看"的
很多人以为地址打印出来只是参考,其实调试器里你可以直接操作内存:
# GDB 调试器示例
(gdb) x/16xb 0x7fff5a3b # 查看该地址的 16 字节
(gdb) set {int}0x7fff5a3b = 42 # 直接往那个地址写值
更直接的例子——游戏修改器(Cheat Engine):
- 搜索生命值
100 - 找到内存地址
0x12345678 - 直接改成
9999
这就是在直接访问内存地址。
6. 真正限制你的是操作系统
现代操作系统有虚拟内存保护——每个进程有自己的地址空间,你不能随便读别的进程的内存。但这跟"能不能读懂地址"是两回事:
- 调试器:可以访问被调试进程的任意地址
- C 语言指针:
*(int*)0x12345678 = 42;直接写 - 系统调用:
/proc/pid/mem可以读任意进程内存
总结
- 二进制是硬件的语言——通/断两种状态最稳定
- 十六进制是人类读二进制的速记法——4 位压缩成 1 位
- 进制只在显示时存在——CPU 内部永远是 0 和 1
- 地址是可以用的——调试器、指针都能直接操作内存地址
- 限制你的是操作系统,不是进制本身
一句话:二进制是计算机的母语,十六进制是人类的翻译器。