👇😊 C语言共用体(Union) 😊👇


🌈 1. 共用体是什么?

现在你有一个多功能储物箱🎁,同一时间只能放一种物品(比如书、水杯或零食),但箱子会根据物品自动调整内部空间。共用体(Union)就是这样的“箱子”:

  • 特点:所有成员共享同一块内存,同一时间只能使用一个成员!
  • 目的:节省内存,灵活处理不同类型数据(但不能同时存储)

🌈 2. 共用体 vs 结构体:关键区别

共用体(Union) 结构体(Struct)
内存分配 所有成员共享同一块内存 每个成员有独立内存
内存大小 等于最大成员的大小 等于所有成员大小之和(考虑对齐)
使用场景 同一时间只存一种类型数据 同时存储多个类型数据

🌈 3. 共用体的定义与内存分配

3.1 定义共用体

📝 语法

1
2
3
4
5
union 共用体标签 {
数据类型 成员1;
数据类型 成员2;
// ...
} 变量名; // 可选变量声明

🌰 例子

1
2
3
4
5
6
7
union Data {
int i; // 4字节
float f; // 4字节
char str[20]; // 20字节 → 共用体总大小=20字节
};

union Data data; // 声明共用体变量

3.2 内存大小验证

1
printf("共用体大小:%zu\n", sizeof(union Data)); // 输出20

🌈 4. 共用体成员访问

4.1 基本访问

使用 . 运算符访问成员(与结构体相同):

1
2
3
4
5
6
7
8
data.i = 10;       // 存入整数
printf("%d\n", data.i);

data.f = 3.14; // 存入浮点数 → 覆盖之前的数据!
printf("%f\n", data.f);

strcpy(data.str, "Hello"); // 存入字符串 → 覆盖浮点数!
printf("%s\n", data.str);

4.2 重要特性

  • 内存共享:修改一个成员会覆盖其他成员的值!
  • 同一时间只能用一种成员,否则数据会混乱!

🌰 错误示例

1
2
3
data.i = 10;
data.f = 3.14;
printf("%d", data.i); // 输出的是被覆盖后的垃圾值!❌

正确用法

1
2
3
4
5
data.i = 10;
printf("%d", data.i); // 先使用i

data.f = 3.14;
printf("%f", data.f); // 再使用f(此时i已被覆盖)

🌈 5. 共用体的应用场景

  1. 节省内存:处理多个类型数据但不同时使用的情况
    • 如:嵌入式设备中,根据模式存储温度(float)或状态码(int)
  2. 解析不同数据格式
    • 如:网络协议中,同一字段可能是IP地址(字符串)或错误码(整数)
  3. 类型转换黑科技
    • 通过共用体直接将整数的内存解释为浮点数(谨慎使用!)

🌰 类型转换示例

1
2
3
4
5
6
7
8
union Converter {
int i;
float f;
};

union Converter c;
c.i = 1092616192; // 0x41200000 的十进制
printf("%f\n", c.f); // 输出10.0(解释为浮点数)

🌈 6. 共用体的初始化

共用体只能初始化第一个成员

1
2
union Data data = {10}; // 正确 → 初始化i为10
// union Data data = {3.14}; ❌ 错误!必须初始化第一个成员

其他成员需后续赋值:

1
data.f = 3.14; // 正确,但会覆盖i的值

🌈 7. 共用体与结构体嵌套

共用体可以作为结构体的成员,反之亦然。

🌰 例子:根据数据类型存储不同信息

1
2
3
4
5
6
7
8
9
10
11
12
struct SensorData {
int type; // 数据类型标识
union {
int intValue;
float floatValue;
char stringValue[20];
} data; // 共用体成员
};

struct SensorData sd;
sd.type = 1; // 1表示存储浮点数
sd.data.floatValue = 36.5;

🌈 8. 常见错误与避坑指南

  1. 同时使用多个成员

    1
    2
    3
    4
    union Data data;
    data.i = 10;
    data.f = 3.14; // i的值被覆盖!
    printf("%d", data.i); // 输出错误数据 ❌
  2. 未初始化直接使用

    1
    2
    union Data data;
    printf("%d", data.i); // 随机值!可能引发问题 ❌
  3. 错误计算内存大小

    1
    2
    3
    4
    5
    union Data {
    int i;
    double d; // 8字节 → 共用体总大小=8
    };
    printf("%zu", sizeof(union Data)); // 正确输出8 ✅

🌈 9. 综合练习

练习1:类型转换器

用共用体实现一个整数和浮点数的互相转换器,验证内存共享特性。

练习2:协议解析

定义一个共用体 Packet,包含整数ID、浮点数值和字符串消息,根据类型字段解析数据。

练习3:错误修复

以下代码有什么问题?

1
2
3
4
5
6
7
8
9
10
11
union Value {
int num;
char str[10];
};

int main() {
union Value v = {5};
strcpy(v.str, "Hello");
printf("%d", v.num); // 输出什么?
return 0;
}

🎯 参考答案

练习1

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

union Converter {
int i;
float f;
};

int main() {
union Converter c;
c.i = 1092616192; // 对应浮点数10.0的二进制
printf("浮点数值:%f\n", c.f); // 输出10.0
return 0;
}

练习2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <string.h>

typedef struct {
int type; // 0:int, 1:float, 2:string
union {
int intValue;
float floatValue;
char stringValue[20];
} data;
} Packet;

void printPacket(Packet p) {
switch(p.type) {
case 0: printf("Int: %d\n", p.data.intValue); break;
case 1: printf("Float: %f\n", p.data.floatValue); break;
case 2: printf("String: %s\n", p.data.stringValue); break;
}
}

int main() {
Packet p1 = {0, .data.intValue = 100};
Packet p2 = {2};
strcpy(p2.data.stringValue, "Hello");
printPacket(p1);
printPacket(p2);
return 0;
}

练习3

问题:存入字符串后,整数成员 num 的值被覆盖,输出的是字符串的内存解释值(垃圾值)。
修正:确保同一时间只使用一个成员!


🚀 总结

  • 共用体是“内存共享大师”,节省内存但需谨慎使用!
  • 同一时间只能激活一个成员,否则数据会“打架”!
  • 适合处理类型多变但不同时使用的数据场景

记住:多动手写代码,感受内存共享的神奇与陷阱! 💻🔧