C语言数据在内存中的存储:补码、大小端与浮点数

C语言数据在内存中的存储:补码、大小端与浮点数
LichermioneC语言数据在内存中的存储
1. 整数类型与补码
整数类型包括 char、short、int、long、long long,并分为有符号和无符号版本。具体宽度由实现决定,可通过 sizeof 与 <limits.h> 查询。
计算机通常使用补码保存有符号整数:正数补码与原码相同;负数由原码符号位外取反后加1得到。
1 | signed char a = -1; // 常见8位表示为11111111 |
无符号整数按模 2^N 运算。有符号整数溢出属于未定义行为。
2. 整型提升
小于 int 的整数类型参与表达式时,通常先提升为 int 或 unsigned int。
1 | char a = 3; |
分析表达式时不能只看变量声明,还要考虑整型提升和通常算术转换。
3. 大小端字节序
对于 0x11223344:
- 大端:低地址存高有效字节
11 22 33 44; - 小端:低地址存低有效字节
44 33 22 11。
判断程序:
1 | int is_little_endian(void) |
网络协议通常使用大端字节序,网络编程中可用 htons/htonl 和 ntohs/ntohl 转换。
4. 浮点数存储
现代平台通常采用 IEEE 754。单精度浮点数常由三部分组成:
S:符号位;E:带偏置的指数;M:有效数字的小数部分。
其基本形式可理解为:
1 | (-1)^S × M × 2^E |
浮点数不能精确表示所有十进制小数,例如 0.1 的二进制表示无限循环。因此不能总用 == 判断计算结果。
1 |
|
5. 类型解释与别名
同一组字节被不同类型解释,会产生完全不同的数值。不要通过不兼容类型指针随意读取对象,这可能违反严格别名规则。需要检查对象表示时,使用 unsigned char * 或 memcpy 更稳妥。
1 | float value = 1.0f; |
6. 总结
底层存储问题要区分“数值”“对象类型”和“字节表示”。补码解释整数,大小端决定多字节排列,IEEE 754 描述浮点编码;跨平台通信和二进制文件尤其需要明确这些差异。