Union在C语言中如何正确使用?

99ANYc3cd6
预计阅读时长 16 分钟
位置: 首页 C语言 正文

什么是联合体?

联合体(union)是一种用户自定义的数据类型,与结构体(struct)非常相似,它允许你在同一个内存位置存储不同类型的数据

union c 语言
(图片来源网络,侵删)

与结构体不同,结构体为每个成员分配独立的内存空间,而联合体的所有成员共享同一段内存空间

核心思想: 在任意时刻,联合体中只存储一个成员的值,当你给一个成员赋值时,其他成员的值也会随之改变,因为它们指向的是同一块内存。


union 的语法

定义一个联合体与定义一个结构体非常相似,只是关键字从 struct 换成了 union

// 定义一个名为 Data 的联合体
union Data {
    int i;
    float f;
    char str[20];
};
// 声明一个联合体变量
Data data;

union 的工作原理:内存共享

这是理解 union 的关键,我们通过一个例子来看内存是如何分配的。

union c 语言
(图片来源网络,侵删)

假设我们有以下联合体:

#include <stdio.h>
union Test {
    char c;
    int i;
    double d;
};
int main() {
    union Test test;
    printf("Size of union Test: %zu bytes\n", sizeof(test));
    printf("Size of char c: %zu bytes\n", sizeof(test.c));
    printf("Size of int i: %zu bytes\n", sizeof(test.i));
    printf("Size of double d: %zu bytes\n", sizeof(test.d));
    return 0;
}

输出结果(在大多数系统上):

Size of union Test: 8 bytes
Size of char c: 1 bytes
Size of int i: 4 bytes
Size of double d: 8 bytes

分析: 联合体 Test 的大小是其最大成员的大小,在这个例子中,double 类型最大,占用 8 字节,所以整个联合体 test 也只占用 8 字节。

当你给 test.i 赋值时,这 8 字节中的前 4 个字节被使用;当你给 test.d 赋值时,全部 8 个字节被使用,它们都从同一块内存的起始地址开始操作。

union c 语言
(图片来源网络,侵删)

union vs. struct:关键区别

这是一个非常常见的面试题,我们用一个表格来清晰地对比它们:

特性 struct (结构体) union (联合体)
内存分配 为每个成员分配独立的内存空间,所有成员的内存累加起来构成结构体的总大小。 所有成员共享同一段内存空间,联合体的大小是其最大成员的大小。
总大小 sizeof(struct) = sizeof(member1) + sizeof(member2) + ... sizeof(union) = max(sizeof(member1), sizeof(member2), ...)
数据存储 可以同时存储所有成员的值,互不干扰。 在任意时刻,只能存储一个成员的值,对一个成员赋值会覆盖其他成员的值。
用途 用于将不同但相关的数据组合成一个单一的对象,一个学生的信息(姓名、年龄、学号)。 用于需要在不同类型间切换,且在同一时间只需要一种类型数据的场景,主要用于节省内存或实现特定功能。

union 的实际应用场景

union 主要用于以下两种情况:

节省内存

当你需要处理一个数据,它有多种可能的类型,但在任何时刻都只有一种类型会被使用时,使用 union 可以大大节省内存。

经典例子: 一个简单的计算器程序,可以处理整数、浮点数和字符串。

#include <stdio.h>
#include <string.h>
// 定义一个联合体来存储不同类型的操作数
union Operand {
    int i_val;
    double f_val;
    char str_val[50];
};
// 定义一个联合体来存储不同类型的运算符
// 这里我们用一个字符来表示,但也可以更复杂
union Operator {
    char op_char;
    // ... 可以有其他类型的操作符
};
int main() {
    // 假设我们正在处理一个整数加法
    union Operand op1, op2;
    op1.i_val = 10;
    op2.i_val = 20;
    printf("Integer Addition: %d + %d = %d\n", op1.i_val, op2.i_val, op1.i_val + op2.i_val);
    // 现在假设我们处理一个浮点数乘法
    // 注意:直接使用 op1.f_val 会覆盖掉 i_val
    op1.f_val = 2.5;
    op2.f_val = 4.0;
    printf("Float Multiplication: %.2f * %.2f = %.2f\n", op1.f_val, op2.f_val, op1.f_val * op2.f_val);
    return 0;
}

类型转换(或称“类型双关”,Type Punning)

这是 union 一个非常强大但也容易出错的用法,你可以利用 union 来读取一个变量以不同的数据类型解释其内存内容。

经典例子: 获取一个 float 变量的二进制表示(IEEE 754 格式)。

#include <stdio.h>
// 联合体允许我们以 int 的方式查看 float 的内存
union FloatToInt {
    float f;
    unsigned int i;
};
int main() {
    union FloatToInt converter;
    converter.f = 3.14f;
    printf("Float value: %f\n", converter.f);
    printf("Its memory representation as an integer (hex): 0x%X\n", converter.i);
    return 0;
}

注意: 这种“类型双关”行为在 C 标准中被称为“未定义行为”(Undefined Behavior, UB),因为它绕过了类型系统,虽然它在几乎所有现代编译器上都能按预期工作,但严格来说并不符合标准,C11 引入了 memcpy 作为更安全、更标准的替代方案。


使用 union 的注意事项

  1. 数据覆盖: 这是最重要的一点,给联合体的一个成员赋值,会立即影响所有其他成员,你必须清楚地知道当前存储的是哪种类型。

  2. 需要额外的信息来追踪类型: 因为联合体本身不记录当前存储的是哪种数据,所以你需要一个额外的变量(通常是 enum)来标记当前活跃的成员。

    enum DataType { INT_TYPE, FLOAT_TYPE, STRING_TYPE };
    struct Data {
        enum DataType type;
        union Value {
            int i;
            float f;
            char s[20];
        } val;
    };
    // 使用时
    struct Data data;
    data.type = INT_TYPE;
    data.val.i = 100;
    // 读取时
    if (data.type == INT_TYPE) {
        printf("The integer value is: %d\n", data.val.i);
    }
  3. 对齐问题: 联合体的大小是其最大成员的大小,但要考虑内存对齐,在某些架构上,为了性能,编译器可能会在成员之间填充一些字节,sizeof(union) 可能会比最大成员的大小还要大一点点。

  4. 初始化: 你只能初始化联合体的第一个成员

    union Data data = {10}; // 正确,初始化了 int i
    // union Data data = {3.14}; // 错误!不能初始化 float f
    // union Data data = {.f = 3.14}; // C99 及以后版本支持,但通常推荐只初始化第一个成员

  • union 是什么? 一种能让不同类型数据共享同一块内存的自定义类型。
  • 核心特点: 内存共享,大小等于最大成员的大小。
  • struct 的核心区别: struct 是内存叠加,union 是内存重叠。
  • 主要用途:
    1. 在数据类型互斥的场景下节省内存。
    2. 实现类型双关,用于底层数据操作(需谨慎)。
  • 使用要点: 必须用额外的机制(如 enum)来追踪当前存储的数据类型,以避免错误地读取被覆盖的数据。
-- 展开阅读全文 --
头像
织梦模板文件夹在哪?如何修改?
« 上一篇 04-23
织梦5.7通杀漏洞利用原理是什么?
下一篇 » 04-23

相关文章

取消
微信二维码
支付宝二维码

目录[+]