C语言union初始化方式有哪些?

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

什么是 union

union 是一种特殊的自定义数据类型,它允许你在相同的内存位置存储不同类型的数据,与 struct(结构体)不同,struct 会为每个成员分配独立的内存空间,而 union 的所有成员共享同一块内存

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

union 的大小等于其最大成员的大小,在任何时刻,union 中只存放一个成员的值,对其中一个成员的赋值会覆盖其他成员的值。

一个简单的 union 示例:

#include <stdio.h>
// 定义一个 union
DataUnion {
    int i;
    float f;
    char c;
};
int main() {
    DataUnion data;
    data.i = 10;
    printf("data.i = %d\n", data.i); // 输出: data.i = 10
    printf("data.f = %f\n", data.f); // 输出: data.f = 0.000000 (值被覆盖,无意义)
    printf("data.c = %c\n", data.c); // 输出: data.c =  (值被覆盖,无意义)
    printf("--------------------\n");
    data.f = 3.14f;
    printf("data.i = %d\n", data.i); // 输出: data.i = 1078530011 (值被覆盖,无意义)
    printf("data.f = %f\n", data.f); // 输出: data.f = 3.140000
    printf("data.c = %c\n", data.c); // 输出: data.c =  (值被覆盖,无意义)
    return 0;
}

union 的初始化

union 的初始化规则与 struct 类似,但有几个关键点需要注意。

1 C89/C90 标准

在较老的 C89/C90 标准中,union 的初始化方式非常受限:你只能使用其第一个成员的值进行初始化

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

语法格式:

union tag_name variable_name = { initial_value_for_first_member };

示例:

#include <stdio.h>
DataUnion data = { 100 }; // 合法:使用第一个成员 int i 的值进行初始化
// DataUnion data2 = { 3.14f }; // 非法:不能使用第二个成员 float f 的值初始化
// DataUnion data3 = { 'A' };  // 非法:不能使用第三个成员 char c 的值初始化

为什么会有这个限制? 因为编译器需要知道如何将初始值存入 union 的内存中,由于所有成员共享内存,从第一个成员开始初始化是最直接、最明确的方式。

2 C99 及以后标准

从 C99 标准开始,union 的初始化规则得到了极大的放宽,你可以使用花括号 列表来初始化 union 的任意成员。

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

语法格式:

// 方式一:使用指定成员初始化 (推荐)
union tag_name variable_name = {
    .member_name = initial_value
};
// 方式二:使用成员顺序初始化 (类似于结构体)
union tag_name variable_name = { initial_value_for_any_member };

示例:

#include <stdio.h>
DataUnion data1 = { .f = 3.14f }; // 合法:使用指定成员 float f 进行初始化
DataUnion data2 = { .c = 'X' };  // 合法:使用指定成员 char c 进行初始化
DataUnion data3 = { 200 };       // 合法:C99 允许,等同于初始化第一个成员 int i

使用指定成员初始化(.member_name)的优点:

  1. 可读性强:代码清晰地表明了你想初始化哪个成员。
  2. 安全性高:即使你重新定义 union,调整了成员的顺序,这段初始化代码依然能正确工作。
  3. 可初始化任意成员:这是最大的好处,不再局限于第一个成员。

代码示例:C99 标准下的初始化

下面是一个完整的、对比不同初始化方式的示例。

#include <stdio.h>
// 定义一个 union
DataUnion {
    int i;
    float f;
    char c;
};
int main() {
    // --- 1. 使用指定成员初始化 (C99 及以后) ---
    printf("--- 使用指定成员初始化 ---\n");
    DataUnion data1 = { .i = 42 }; // 初始化 int 成员
    printf("data1.i = %d\n", data1.i);
    printf("data1.f = %f (无意义)\n", data1.f);
    printf("data1.c = %c (无意义)\n\n", data1.c);
    DataUnion data2 = { .f = 2.71828f }; // 初始化 float 成员
    printf("data2.f = %f\n", data2.f);
    printf("data2.i = %d (无意义)\n", data2.i);
    printf("data2.c = %c (无意义)\n\n", data2.c);
    DataUnion data3 = { .c = 'Z' }; // 初始化 char 成员
    printf("data3.c = %c\n", data3.c);
    printf("data3.i = %d (无意义)\n", data3.i);
    printf("data3.f = %f (无意义)\n\n", data3.f);
    // --- 2. 使用成员顺序初始化 (C99 及以后) ---
    // 这等同于初始化第一个成员
    printf("--- 使用成员顺序初始化 ---\n");
    DataUnion data4 = { 999 }; // 初始化第一个成员 int i
    printf("data4.i = %d\n", data4.i);
    printf("data4.f = %f (无意义)\n", data4.f);
    printf("data4.c = %c (无意义)\n\n");
    // --- 3. 不使用初始化列表(默认初始化) ---
    // 注意:union 的默认初始化是未定义行为
    // 不同编译器可能有不同表现,不要依赖它
    printf("--- 默认初始化 (不推荐) ---\n");
    DataUnion data5;
    // data5 的值是未定义的,直接使用是危险的
    // printf("data5.i = %d\n", data5.i); // 可能打印出垃圾值
    printf("请勿依赖默认初始化,\n");
    return 0;
}

重要注意事项

  1. 访问被覆盖的成员是危险的 当你向 union 的一个成员写入数据后,再读取另一个成员的值,得到的是该内存被解释为另一种类型后的结果,这通常不是你想要的,甚至可能导致程序崩溃。

  2. 初始化列表长度 你不能提供超过一个成员的初始值,编译器会报错。

    // 错误示例
    DataUnion data_error = { .i = 10, .f = 3.14 }; // 错误:太多初始化项
  3. 默认初始化 如果像声明普通变量一样声明 union 而不进行初始化(如 DataUnion my_data;),它的内容是未定义的,你不能假设它的成员是零,这是与 struct 不同的地方(全局/静态的 struct 默认初始化为零,但 union 不是)。

标准 初始化方式 说明
C89/C90 union U u = { value }; 只能用第一个成员的值进行初始化。
C99 及以后 union U u = { .member = value }; 推荐,使用指定成员进行初始化,灵活且安全。
C99 及以后 union U u = { value }; 允许,效果等同于初始化第一个成员。
任何标准 union U u; 默认初始化未定义的,不要依赖。

最佳实践: 在现代 C 编程(遵循 C99 或更新标准)中,强烈推荐使用指定成员的初始化方式(.member_name = value,这种方式最清晰、最安全,也最能体现 union 的意图。

-- 展开阅读全文 --
头像
dede文章缩略图尺寸如何自定义调整?
« 上一篇 03-04
织梦手机版图片如何自适应?
下一篇 » 03-04

相关文章

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

目录[+]