C语言Union中int与double未相互覆盖的原因咨询
问题
C语言中Union类型的内存是重叠的,同一时间仅应存储一种类型的对象。但实验发现,当Union包含int和double成员时,修改其中一个成员的值后,另一个成员的输出值似乎未被破坏,仿佛内存没有重叠。
实验代码如下:
#include <stdlib.h> #include <stdio.h> #include <limits.h> union Data { int int_val; double d_val; }; int main() { union Data data = {0}; data.d_val = 0; data.int_val = INT_MAX; printf("%f, %d\n", data.d_val, data.int_val); data.d_val = __DBL_MAX__; data.int_val = 0; printf("%f, %d\n", data.d_val, data.int_val); return EXIT_SUCCESS; }
编译命令为gcc -g -o main main.c,运行结果:
0.000000, 2147483647 179769227765543015871544843204898548561652852981845720830741485657701606177416873137818677866537230765451463672297559953407043753842793767107723942762853526789169461948323286682454336232649253620483947928451548544466350813686932742283772637216475668546847194115014476747609904655041063083840261631978879582208.000000, 0
为何修改一个成员后,另一个成员的输出值未出现预期的破坏?
原因分析
Union的内存确实是完全重叠的,出现这种现象是数据存储特性和输出格式掩盖了内存变化,核心原因如下:
1. 数据类型的字节长度与存储格式差异
通常int占4字节,double占8字节,且double遵循IEEE754浮点标准:
- 高字节存储符号位和指数,低字节存储尾数的低位部分。
- 当你修改
int_val时,仅覆盖了Union内存的前4字节(double的低4字节),对double的核心数值部分(指数和尾数高位)影响极小甚至可忽略。
以第一次操作为例:
- 先将
d_val设为0.0,此时Union的8字节内存全为0。 - 再将
int_val设为INT_MAX(二进制为01111111 11111111 11111111 11111111),仅修改前4字节。 - 此时8字节的二进制对应一个IEEE754非规格化数,其数值极其接近0,
printf用%f格式输出时会将其近似为0.0,导致你误以为d_val未被修改。
2. 大数的低位修改无法被输出感知
第二次操作中:
- 先将
d_val设为__DBL_MAX__,这是一个极大的浮点数,其尾数的低位部分对整体数值的影响微乎其微。 - 再将
int_val设为0,仅覆盖前4字节(double的尾数低位),这种修改带来的数值变化远小于%f格式的显示精度,因此输出时完全看不出差异。
3. Union的访问规则
C标准明确,Union中只有最后被赋值的成员是“活跃”的,访问其他成员属于未定义行为(编译器会直接按内存解析)。你的实验中看似另一个成员未被破坏,本质是内存修改的部分对该类型的数值表现影响极小,被输出格式掩盖,并非内存没有重叠。
验证方法
若要直观看到内存变化,可直接打印Union的所有内存字节:
// 在main函数中添加这段代码,修改成员后打印内存 unsigned char *p = (unsigned char *)&data; for (int i = 0; i < sizeof(union Data); i++) { printf("%02x ", p[i]); } printf("\n");
运行后会看到,修改int_val后,Union的内存确实发生了变化,只是double的输出表现不明显。
内容的提问来源于stack exchange,提问作者Arthur Grigoryan
相关产品推荐
相关产品推荐

