数据在计算机中的表示
计算机底层只认识 0 和 1,所有数据——数字、文字、图像、声音——最终都要编码成二进制。理解这些编码方式,能帮助你写出更高效的 C 程序,也能避免许多与类型、溢出相关的隐蔽错误。
二进制、八进制与十六进制
二进制是计算机的基础,每一位只能是 0 或 1。八进制和十六进制是二进制的紧凑写法,在 C 语言中直接支持:
int dec = 42; /* 十进制 */
int oct = 052; /* 八进制:以 0 开头,等于十进制 42 */
int hex = 0x2A; /* 十六进制:以 0x 开头,等于十进制 42 */
printf("%d %d %d\n", dec, oct, hex); /* 输出:42 42 42 */
printf("%o %x\n", dec, dec); /* 输出:52 2a */
十六进制特别适合表示内存地址和位模式,因为每 4 位二进制恰好对应 1 位十六进制数字:0xFF = 1111 1111。
位、字节与字
**位(bit)**是信息的最小单位,取值 0 或 1。
**字节(byte)**由 8 个位组成,是内存寻址的最小单位。C 语言中 sizeof(char) 恒为 1,而 1 字节等于 8 位(CHAR_BIT 在 <limits.h> 中定义,标准规定至少为 8)。
#include <limits.h>
printf("CHAR_BIT = %d\n", CHAR_BIT); /* 通常是 8 */
printf("sizeof(char) = %zu\n", sizeof(char)); /* 恒为 1 */
**字(word)**是 CPU 一次能处理的整数位数,与架构相关:32 位系统的字长是 32 位(4 字节),64 位系统是 64 位(8 字节)。C 语言中的 int 通常等于机器字长,但标准只保证它至少为 16 位。
整数的存储模型
整数在内存中以二进制补码(Two's Complement)形式存储,这是现代计算机的通用标准。
无符号整数:直接按二进制存储,所有位都参与表示数值。
unsigned char a = 200; /* 二进制:1100 1000 */
有符号整数:最高位是符号位,0 表示正,1 表示负。负数的表示采用补码——对其绝对值的二进制取反再加 1。
signed char b = -5;
/* 5 的二进制:0000 0101 */
/* 取反: 1111 1010 */
/* 加 1: 1111 1011 → 这就是 -5 的补码表示 */
补码的优势在于:加减法可以用同一套电路处理,符号位自然参与运算。
signed char x = 127;
signed char y = x + 1; /* 0111 1111 + 1 = 1000 0000 = -128,溢出! */
有符号整数溢出在 C99 中是未定义行为,编译器可能做出各种优化假设,实际结果取决于具体平台和编译器。
浮点数的存储模型
浮点数采用 IEEE 754 标准,将二进制科学计数法编码为三部分:
- 符号位:1 位,0 为正,1 为负
- 指数位:存储指数(经过偏移处理)
- 尾数位:存储小数部分
float f = 0.1f;
/* 实际存储的二进制值并非精确的 0.1 */
printf("%.20f\n", f); /* 输出:0.10000000149011611938 */
由于二进制无法精确表示某些十进制小数(如 0.1、0.2),浮点数运算存在固有精度误差:
if (0.1 + 0.2 == 0.3) /* 可能为假! */
printf("Equal\n");
else
printf("Not equal\n"); /* 通常输出这个 */
/* 正确做法:使用误差范围 */
#define EPSILON 1e-6
if (fabs((0.1 + 0.2) - 0.3) < EPSILON)
printf("Approximately equal\n");
float 通常占 4 字节(32 位),有效精度约 7 位十进制数字;double 占 8 字节(64 位),有效精度约 15 位。科学计算和对精度敏感的场景应优先使用 double。
字符的编码
字符在计算机中也是数字。ASCII 编码用 7 位表示 128 个字符:数字 0–9 对应 48–57,大写字母 A–Z 对应 65–90,小写字母 a–z 对应 97–122。
char c = 'A';
printf("%d\n", c); /* 输出 65 */
printf("%c\n", c + 32); /* 输出 'a':大写转小写 */
/* 判断字符类型 */
if (c >= '0' && c <= '9')
printf("Digit\n");
if (c >= 'A' && c <= 'Z')
printf("Uppercase\n");
C99 引入了宽字符(wchar_t)和多字节字符支持,用于处理 ASCII 之外的字符集(如中文、日文)。