request-free-img

C语言中联合体(Union)与结构体(Struct)的本质区别

在C语言中,联合体(Union)和结构体(Struct)从语法形式上看非常相似,但它们的实际用途和内存分配机制却完全不同。如果稍不注意,就很容易引发程序的致命错误。

1. 先理解结构体(Struct)

结构体是一种将不同类型的数据组合在一起的数据结构,每个成员都拥有独立的内存空间。

#include <iostream>
struct DataStruct {
    int i;
    float f;
    char str[20];
};

假设我们有这样一个结构体 DataStruct,它包含三个成员变量:

  • int i —— 占用4个字节
  • float f —— 占用4个字节
  • char str[20] —— 占用20个字节

在忽略内存对齐的情况下,结构体的大小是所有成员大小的总和,即 4 + 4 + 20 = 28个字节

我们可以通过代码来验证:

int size1 = sizeof(DataStruct);
std::cout << "size of DataStruct: " << size1 << std::endl;

同时,我们还可以查看每个成员变量的内存地址:

DataStruct dataStruct;
char* i_addr = (char*)&dataStruct.i;
char* f_addr = (char*)&dataStruct.f;
char* str_addr = (char*)&dataStruct.str;

printf("i_addr: %p\n", i_addr);
printf("f_addr: %p\n", f_addr);
printf("str_addr: %p\n", str_addr);

运行结果会显示:f变量的地址刚好在i变量地址之后4个字节,str变量的地址又在f变量之后4个字节。这说明结构体中的成员在内存中是依次连续排列的。

2. 联合体(Union)的内存机制

现在我们来看联合体:

union DataUnion {
    int i;
    float f;
    char str[20];
};

同样使用上述三个成员,但当我们用 sizeof(DataUnion) 计算其大小时,却发现它只占用 20个字节——也就是最大成员 char str[20] 所占用的空间。

联合体的关键特性是:

  • 所有成员共用同一块内存
  • 联合体的大小由其最大的成员决定
  • 同一时间只能有效存储一个成员的值

3. 联合体成员地址与值覆盖演示

我们来看下面的代码:

#include <iostream>
union DataUnion {
    int i;
    float f;
    char str[20];
};

int main() {
    int size1 = sizeof(DataUnion);
    std::cout << "size of DataUnion: " << size1 << std::endl;

    DataUnion dataUnion = { 0 };
    std::cout << "i= " << dataUnion.i << std::endl;
    std::cout << "f= " << dataUnion.f << std::endl;

    dataUnion.i = 10;
    std::cout << "f= " << dataUnion.f << std::endl;

    dataUnion.f = 20;
    std::cout << "i= " << dataUnion.i << std::endl;

    return 1;
}

运行后你会发现:

  • 三个成员变量的内存地址是完全相同
  • 当修改 i 的值后,f 的值会发生变化
  • 当修改 f 的值后,i 的值也会随之改变

这是因为它们共享同一块内存,只是以不同的数据类型去解读这块内存中的比特位。

4. 联合体的实际应用场景

联合体的内存共享特性使其在以下场景中非常有用:

  • 需要存储多种数据类型,但在同一时间只需要使用其中一种时
  • 解析网络数据包或硬件寄存器值(不同方式解读同一内存)
  • 节省内存空间(尤其在嵌入式系统中)

总结

结构体(Struct):成员各自拥有独立内存,适合把相关数据打包在一起。

联合体(Union):所有成员共享同一块内存,大小由最大成员决定,适合多种类型互斥使用的场景。

理解两者在内存布局上的根本区别,是正确使用联合体的关键。使用时一定要清楚当前正在操作的是哪一个成员,否则很容易引发难以调试的内存错误。


更多问题探讨,请关注公众号:程序员角