首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【C语言】union 关键字详解

【C语言】union 关键字详解

作者头像
LuckiBit
发布2024-12-11 10:58:36
发布2024-12-11 10:58:36
4560
举报
文章被收录于专栏:C语言C语言

C语言 union 关键字详解

union 关键字在C语言中用于定义联合体(union)。联合体是一种特殊的数据结构,它允许在同一内存位置存储不同的数据类型。不同于结构体(struct),联合体的所有成员共享相同的内存区域,因此联合体的大小等于其最大成员的大小。

1. union 关键字的基本概念

1.1 基本语法
代码语言:javascript
复制
union union_name {
    type1 member1;
    type2 member2;
    // ...
};
  • union_name:联合体的名称。
  • type1, type2, …:联合体的成员类型。
  • member1, member2, …:联合体的成员名称。
1.2 示例
代码语言:javascript
复制
#include <stdio.h>

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

int main() {
    union Data data;

    data.i = 10;
    printf("data.i: %d\n", data.i); // 输出: data.i: 10

    data.f = 220.5;
    printf("data.f: %f\n", data.f); // 输出: data.f: 220.500000

    strcpy(data.str, "Hello");
    printf("data.str: %s\n", data.str); // 输出: data.str: Hello

    // 注意:访问其他成员可能会导致未定义行为
    printf("data.i: %d\n", data.i); // 输出: data.i: (可能是未定义的值)

    return 0;
}

解释

  • union Data 定义了一个联合体,它包含一个 int、一个 float 和一个字符数组 str
  • 联合体的成员共享相同的内存,因此在写入 data.i 后,写入 data.f 会覆盖 data.i 的值。
  • 访问覆盖的成员可能会得到未定义的结果。

输出

代码语言:javascript
复制
data.i: 10
data.f: 220.500000
data.str: Hello
data.i: 0 (或其他未定义值)

2. union 关键字的大小

2.1 大小的计算

联合体的大小等于其最大成员的大小,加上可能的内存对齐要求。因为所有成员共享同一块内存,联合体的大小由其最大的成员决定。

2.1.1 示例:计算联合体的大小
代码语言:javascript
复制
#include <stdio.h>

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

int main() {
    printf("Size of union Data: %zu\n", sizeof(union Data)); // 输出: Size of union Data: 20
    return 0;
}

解释

  • sizeof(union Data) 返回联合体 Data 的大小。
  • 在这个示例中,str 的大小决定了联合体的大小,因此输出是 20 字节。

输出

代码语言:javascript
复制
Size of union Data: 20
2.2 内存对齐

联合体的内存对齐取决于编译器的实现和平台。通常,联合体的大小是其最大成员的大小,并可能会对齐到某个边界。

2.2.1 示例:内存对齐
代码语言:javascript
复制
#include <stdio.h>

union AlignedData {
    char c;
    int i;
    double d;
};

int main() {
    printf("Size of union AlignedData: %zu\n", sizeof(union AlignedData)); // 输出: Size of union AlignedData: 8
    return 0;
}

解释

  • sizeof(union AlignedData) 返回联合体 AlignedData 的大小。
  • double 类型的大小通常是 8 字节,因此联合体的大小是 8 字节,并且可能会有内存对齐的要求。

输出

代码语言:javascript
复制
Size of union AlignedData: 8

3. 使用 union 关键字的实际应用

3.1 动态数据存储

联合体适用于需要存储不同数据类型但不需要同时存储的场景。它节省了内存空间,适合在内存受限的系统中使用。

3.1.1 示例
代码语言:javascript
复制
#include <stdio.h>

union SensorData {
    int temperature;
    float pressure;
    char status;
};

int main() {
    union SensorData sensor;

    sensor.temperature = 25;
    printf("Temperature: %d\n", sensor.temperature); // 输出: Temperature: 25

    sensor.pressure = 1013.25;
    printf("Pressure: %.2f\n", sensor.pressure); // 输出: Pressure: 1013.25

    sensor.status = 'A';
    printf("Status: %c\n", sensor.status); // 输出: Status: A

    // 注意:访问其他成员可能会导致未定义行为
    printf("Temperature: %d\n", sensor.temperature); // 输出: Temperature: (可能是未定义的值)

    return 0;
}

解释

  • union SensorData 包含了 intfloatchar 类型的成员。
  • 由于这些成员共享内存,修改一个成员会影响其他成员的值。
  • 联合体的内存大小取决于其最大成员的大小。

输出

代码语言:javascript
复制
Temperature: 25
Pressure: 1013.25
Status: A
Temperature: 0 (或其他未定义值)
3.2 解析复杂数据结构

联合体可以与结构体结合使用,简化复杂数据结构的定义和访问。

3.2.1 示例
代码语言:javascript
复制
#include <stdio.h>

struct Packet {
    unsigned char type;
    union {
        int id;
        float value;
    } data;
};

int main() {
    struct Packet packet;

    packet.type = 1;
    packet.data.id = 123;
    printf("Packet Type: %d\n", packet.type); // 输出: Packet Type: 1
    printf("Packet ID: %d\n", packet.data.id); // 输出: Packet ID: 123

    packet.type = 2;
    packet.data.value = 3.14;
    printf("Packet Type: %d\n", packet.type); // 输出: Packet Type: 2
    printf("Packet Value: %.2f\n", packet.data.value); // 输出: Packet Value: 3.14

    return 0;
}

解释

  • struct Packet 定义了一个包含 typedata 成员的结构体。
  • data 成员是一个联合体,它可以是 intfloat 类型。
  • 通过修改 type 来决定 data 中存储的数据类型。

输出

代码语言:javascript
复制
Packet Type: 1
Packet ID: 123
Packet Type: 2
Packet Value: 3.14

4. union 关键字的注意事项

注意事项

描述

示例

内存共享

联合体的所有成员共享相同的内存位置,因此同时存储多个成员可能会导致数据覆盖。

data.i, data.f, data.str

内存对齐

联合体的大小通常是其最大成员的大小,并且可能会受到内存对齐的影响。

sizeof(union Data)

未定义行为

访问未被初始化的联合体成员或被其他成员覆盖的成员可能会导致未定义行为。

data.i,data.f 的输出

与结构体结合

联合体可以与结构体结合使用,以创建更复杂的数据结构。

struct Packet

5. 综合示例

以下是一个综合示例,展示了联合体在实际应用中的不同用法。

代码语言:javascript
复制
#include <stdio.h>

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

struct Sensor {
    unsigned char id;
    union Data value;
};

int main() {
    union Data data;
    struct Sensor sensor;

    // 使用联合体
    data.i = 100;
    printf("Data as int: %d\n", data.i); // 输出: Data as int: 100
    data.f = 3.14;
    printf("Data as float: %f\n", data.f); // 输出: Data as float: 3.140000
    strcpy(data.str, "Hello");
    printf("Data as string: %s\n", data.str); // 输出: Data as string: Hello

    // 使用结构体结合联合体
    sensor.id = 1;
    sensor.value.f = 99.99;
    printf("Sensor ID: %d\n", sensor.id); // 输出: Sensor ID: 1
    printf("Sensor Value as float: %f\n", sensor.value.f); // 输出: Sensor Value as float: 99.990000

    return 0;
}

**编译和

编译和执行

代码语言:javascript
复制
gcc -o my_program main.c
./my_program

输出结果

代码语言:javascript
复制
Data as int: 100
Data as float: 3.140000
Data as string: Hello
Sensor ID: 1
Sensor Value as float: 99.990000

6. 联合体的内存分布和对齐

联合体的内存分布取决于其最大成员的大小和对齐要求。在不同的系统和编译器上,内存对齐的要求可能不同。

6.1 内存对齐示例

以下示例展示了联合体在内存中的布局以及对齐的影响。

示例
代码语言:javascript
复制
#include <stdio.h>

union Example {
    char c;
    int i;
    double d;
};

int main() {
    union Example ex;

    ex.c = 'A';
    printf("ex.c: %c\n", ex.c); // 输出: ex.c: A
    printf("Size of union Example: %zu\n", sizeof(union Example)); // 输出: Size of union Example: 8

    ex.i = 100;
    printf("ex.i: %d\n", ex.i); // 输出: ex.i: 100
    printf("Size of union Example: %zu\n", sizeof(union Example)); // 输出: Size of union Example: 8

    ex.d = 3.14;
    printf("ex.d: %f\n", ex.d); // 输出: ex.d: 3.140000
    printf("Size of union Example: %zu\n", sizeof(union Example)); // 输出: Size of union Example: 8

    return 0;
}

解释

  • 联合体 Example 包含 charintdouble 三个成员。
  • double 是最大成员,其大小为 8 字节。
  • 因此,整个联合体的大小也是 8 字节。

输出

代码语言:javascript
复制
ex.c: A
Size of union Example: 8
ex.i: 100
Size of union Example: 8
ex.d: 3.140000
Size of union Example: 8
6.2 联合体内存布局的可视化

为了更好地理解联合体的内存布局,可以通过输出各个成员的地址来可视化。

示例
代码语言:javascript
复制
#include <stdio.h>

union Example {
    char c;
    int i;
    double d;
};

int main() {
    union Example ex;

    printf("Address of ex.c: %p\n", (void*)&ex.c);
    printf("Address of ex.i: %p\n", (void*)&ex.i);
    printf("Address of ex.d: %p\n", (void*)&ex.d);
    printf("Size of union Example: %zu\n", sizeof(union Example));

    return 0;
}

解释

  • 通过打印各个成员的地址,可以看到它们共享相同的内存位置。

输出

代码语言:javascript
复制
Address of ex.c: 0x7ffc93c88820
Address of ex.i: 0x7ffc93c88820
Address of ex.d: 0x7ffc93c88820
Size of union Example: 8

7. unionstruct 的对比

特性

union

struct

内存分配

所有成员共享相同的内存位置

每个成员都有自己的内存位置

大小

等于最大成员的大小

等于所有成员大小之和

用途

用于节省内存,适合在同一时间只需要一个成员的情况

用于需要同时访问所有成员的情况

内存对齐

由最大成员决定

每个成员都有自己的内存对齐要求

数据访问

访问一个成员会覆盖其他成员的数据

访问一个成员不会影响其他成员

7.1 示例:unionstruct 的对比
代码语言:javascript
复制
#include <stdio.h>
#include <string.h>

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

struct DataStruct {
    int i;
    float f;
    char str[20];
};

int main() {
    union DataUnion u;
    struct DataStruct s;

    // 初始化并打印联合体成员
    u.i = 10;
    printf("Union - u.i: %d\n", u.i);
    u.f = 220.5;
    printf("Union - u.f: %f\n", u.f);
    strcpy(u.str, "Hello");
    printf("Union - u.str: %s\n", u.str);

    // 初始化并打印结构体成员
    s.i = 10;
    printf("Struct - s.i: %d\n", s.i);
    s.f = 220.5;
    printf("Struct - s.f: %f\n", s.f);
    strcpy(s.str, "Hello");
    printf("Struct - s.str: %s\n", s.str);

    // 联合体的大小
    printf("Size of union DataUnion: %zu\n", sizeof(union DataUnion));
    // 结构体的大小
    printf("Size of struct DataStruct: %zu\n", sizeof(struct DataStruct));

    return 0;
}

输出

代码语言:javascript
复制
Union - u.i: 10
Union - u.f: 220.500000
Union - u.str: Hello
Struct - s.i: 10
Struct - s.f: 220.500000
Struct - s.str: Hello
Size of union DataUnion: 20
Size of struct DataStruct: 28

解释

  • 联合体的大小等于其最大成员的大小(char str[20],即 20 字节)。
  • 结构体的大小等于其所有成员大小之和,并且可能由于内存对齐而增加(在此示例中为 28 字节)。

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

8.1 联合体在硬件编程中的应用

联合体常用于硬件编程,例如在嵌入式系统中表示一个寄存器,它可以同时表示多个不同的字段。联合体在嵌入式系统和硬件编程中广泛使用,因为它允许以位字段的形式直接访问和操作硬件寄存器。

示例:硬件寄存器中的联合体应用
代码语言:javascript
复制
#include <stdio.h>

union Register {
    unsigned int value;
    struct {
        unsigned int flag1 : 1;
        unsigned int flag2 : 1;
        unsigned int flag3 : 1;
        unsigned int reserved : 29;
    } flags;
};

int main() {
    union Register reg;

    // 设置寄存器的值
    reg.value = 0x05;
    printf("Register value: 0x%X\n", reg.value);
    printf("Flag1: %d\n", reg.flags.flag1);
    printf("Flag2: %d\n", reg.flags.flag2);
    printf("Flag3: %d\n", reg.flags.flag3);

    // 修改标志位
    reg.flags.flag1 = 0;
    printf("Register value: 0x%X\n", reg.value);
    printf("Flag1: %d\n", reg.flags.flag1);

    return 0;
}

解释

  • union Register 表示一个硬件寄存器,其中 value 表示寄存器的完整值,而 flags 表示寄存器的各个位字段。
  • 通过修改 flags 中的位字段,可以影响 value 的值,反之亦然。

输出

代码语言:javascript
复制
Register value: 0x5
Flag1: 1
Flag2: 0
Flag3: 1
Register value: 0x4
Flag1: 0
8.2 联合体在协议解析中的应用

联合体也常用于协议解析中,特别是在处理网络数据包或二进制文件时。通过使用联合体,可以在不复制数据的情况下,以多种格式查看相同的数据。

示例:协议解析中的联合体应用
代码语言:javascript
复制
#include <stdio.h>

union Protocol {
    struct {
        unsigned char version;
        unsigned char type;
        unsigned short length;
    } header;
    unsigned char raw[4];
};

int main() {
    union Protocol proto;

    // 初始化原始数据
    proto.raw[0] = 1; // version
    proto.raw[1] = 2; // type
    proto.raw[2] = 0; // length high byte
    proto.raw[3] = 10; // length low byte

    printf("Version: %d\n", proto.header.version); // 输出: Version: 1
    printf("Type: %d\n", proto.header.type); // 输出: Type: 2
    printf("Length: %d\n", proto.header.length); // 输出: Length: 10

    return 0;
}

解释

  • union Protocol 定义了一个协议头部,其中 header 是结构体表示的协议头,raw 是原始字节数组。
  • 通过初始化 raw 数组,可以间接初始化 header 结构体,并且可以使用结构体成员来访问数据。

输出

代码语言:javascript
复制
Version: 1
Type: 2
Length: 10
8.3 联合体在节省内存中的应用

在某些应用场景中,需要在不同时间存储不同类型的数据。通过使用联合体,可以节省内存,因为联合体的所有成员共享相同的内存位置。

示例:节省内存的联合体应用
代码语言:javascript
复制
#include <stdio.h>

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

int main() {
    union Data data;

    data.i = 42;
    printf("data.i: %d\n", data.i); // 输出: data.i: 42

    data.f = 3.14;
    printf("data.f: %f\n", data.f); // 输出: data.f: 3.140000

    snprintf(data.str, 20, "Hello, World!");
    printf("data.str: %s\n", data.str); // 输出: data.str: Hello, World!

    // 注意:访问其他成员可能会导致未定义行为
    printf("data.i: %d\n", data.i); // 输出: data.i: (未定义值)

    return 0;
}

解释

  • union Data 包含了 intfloatchar 数组三种数据类型。
  • 通过依次存储 intfloatchar 数组,可以看到每次存储新的数据时会覆盖之前的数据。

输出

代码语言:javascript
复制
data.i: 42
data.f: 3.140000
data.str: Hello, World!
data.i: 1819043144 (或其他未定义值)

9. 总结

联合体(union)是一种强大的数据结构,在C语言中具有广泛的应用。通过共享内存位置,联合体可以在不同时间存储不同类型的数据,从而节省内存。在嵌入式系统、硬件编程和协议解析等领域,联合体的使用尤为常见。理解和正确使用联合体可以使代码更加高效和灵活,特别是在内存受限的系统中。

9.1 表格总结

特性

描述

示例

内存共享

联合体的所有成员共享相同的内存位置,因此同时存储多个成员可能会导致数据覆盖。

data.i, data.f, data.str

内存对齐

联合体的大小通常是其最大成员的大小,并且可能会受到内存对齐的影响。

sizeof(union Data)

未定义行为

访问未被初始化的联合体成员或被其他成员覆盖的成员可能会导致未定义行为。

data.i,data.f 的输出

与结构体结合

联合体可以与结构体结合使用,以创建更复杂的数据结构。

struct Packet

硬件编程中的应用

联合体常用于表示硬件寄存器,其中每个位字段可以代表寄存器的不同功能。

union Register

协议解析中的应用

联合体常用于处理网络数据包或二进制文件,使得同一块数据可以以多种格式查看。

union Protocol

节省内存的应用

联合体在不同时间存储不同类型的数据,从而节省内存。

union Data

通过对以上内容的学习,您现在应该对C语言中的union关键字有了全面的理解和掌握。希望这些示例和解释能够帮助您在实际编程中更好地应用联合体。

6. 结束语

  1. 本节内容已经全部介绍完毕,希望通过这篇文章,大家对 union 关键字区别有了更深入的理解和认识。
  2. 感谢各位的阅读和支持,如果觉得这篇文章对你有帮助,请不要吝惜你的点赞和评论,这对我们非常重要。再次感谢大家的关注和支持
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-08-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • C语言 union 关键字详解
    • 1. union 关键字的基本概念
      • 1.1 基本语法
      • 1.2 示例
    • 2. union 关键字的大小
      • 2.1 大小的计算
      • 2.2 内存对齐
    • 3. 使用 union 关键字的实际应用
      • 3.1 动态数据存储
      • 3.2 解析复杂数据结构
    • 4. union 关键字的注意事项
    • 5. 综合示例
    • 6. 联合体的内存分布和对齐
      • 6.1 内存对齐示例
      • 6.2 联合体内存布局的可视化
    • 7. union 和 struct 的对比
      • 7.1 示例:union 与 struct 的对比
    • 8. 联合体的实际应用场景
      • 8.1 联合体在硬件编程中的应用
      • 8.2 联合体在协议解析中的应用
      • 8.3 联合体在节省内存中的应用
    • 9. 总结
      • 9.1 表格总结
    • 6. 结束语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档