一文带你认识C语言的联合体和枚举
作者:小容小容
一.联合体
1.联合体的声明
联合体(Union) 是一种特殊的用户自定义数据类型,它允许在同一内存位置存储不同类型的数据,但同时只能存储其中一个成员。联合体的所有成员共享相同的内存空间,因此联合体的大小等于其最大成员的大小。
我们来看一下简单定义,有些类似于结构体.。
#include<stdio.h> union un { int age; char i; }; int main() { return 0; }
虽然说结构上有些类似于结构体,但是看特点的话,他两各不相同,各有所长。
2.联合体的特点
(一)内存共享
联合体的所有成员共享同一块内存地址。意味着不论联合体有多少成员,它们都会使用同一个存储空间。只有一个成员可以在同一时刻存储有效数据。
优点:这种特性使联合体节省内存,特别适合多个数据不会同时使用的场景。
我们来看一个例子:
#include<stdio.h> union un { int age; char i; }; int main() { union un u = { 0 }; u.age = 0x11223344; u.i = 0x55; return 0; }
我们按F10进入逐过程:
查看内存:可以看到u.i的0x55覆盖了u.age的44
可以说明他们的内存是共享的!!!
(二)大小等于最大成员的大小
联合体的大小等于其最大成员的大小。虽然它可以有多个成员,但只会根据其中最大成员的大小来分配内存。
我们看一下例子:
#include<stdio.h> union un { int age; char i; }; int main() { union un u = { 0 }; //计算一下联合体的大小 printf("%zd", sizeof(u)); return 0; }
结果是:
解释:int四个字节,char一个字节,显然最大的是四个字节。联合体的大小即最大成员的大小
另一特殊情况:
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
看一例子:
#include<stdio.h> union un { char name[10]; int age; }; int main() { union un u = { 0 }; printf("%zd", sizeof(u)); return 0; }
我们看一下结果:
可以看到联合体的大小并不是最大成员的大小10(char1个字节,1*10),是12(最大对齐数4,4的整数倍)
这里10个char类型和int类型就已经占用14个字节了,但是结果是12个字节,所以这里也能证明联合体的特点内存共享。
(三)一次只能使用一个成员
联合体(union)的特性是一次只能有效使用一个成员,因为所有成员共享同一块内存。当你向一个成员赋值时,之前赋值的其他成员数据会被覆盖。
其实也就是因为另一个特性内存共享导致的
#include <stdio.h> #include <string.h> union MyUnion { int i; float f; char str[20]; }; int main() { union MyUnion u; // 设置整数值 u.i = 42; printf("设置整数后: u.i = %d\n", u.i); // 设置浮点数值,覆盖整数值 u.f = 3.14; printf("设置浮点型之后: u.f = %.2f\n", u.f); // 整数值被覆盖,无法正常读取 printf("u.i (设置浮点型之后) = %d (corrupted)\n", u.i); // 设置字符串,覆盖浮点数值 strcpy(u.str, "Hello"); printf("设置字符串后: u.str = %s\n", u.str); // 浮点数值被覆盖,无法正常读取 printf("u.f (设置字符串后) = %.2f (corrupted)\n", u.f); return 0; }
结果:
3.联合体相比较于结构体
(一)内存分配
结构体:
- 结构体中的每个成员都有自己独立的内存空间。
- 结构体的总大小等于所有成员大小的总和(加上可能的内存对齐)。
- 成员的内存布局是顺序的,成员之间不会共享内存
联合体:
- 联合体中的所有成员共享同一块内存。
- 联合体的总大小等于其中最大成员的大小。
- 只能同时使用一个成员,存入一个成员的值会覆盖其他成员。
(二)使用方式
结构体:
- 结构体可以同时使用多个成员。每个成员都有独立的存储空间,互不干扰。
- 适合用于描述包含多个相关数据的复杂对象。
(三)内存布局对比
看看这两个代码内存的占用
我们看一下内存分布图:
(四)总结
特性 | 结构体 | 联合体 |
内存分配 | 每个成员都有独立内存,大小为所有成员之和。 | 所有成员共享同一块内存,大小为最大成员。 |
使用多个成员 | 可以同时使用多个成员。 | 一次只能使用一个成员,修改会覆盖其他成员。 |
适用场景 | 同时需要存储和操作多种数据类型。 | 不同时间只需使用一种数据类型,节省内存。 |
访问速度 | 各成员独立访问,互不影响。 | 需要判断当前存储的成员类型,可能增加复杂度。 |
二.枚举
枚举(enum)是一种用户自定义的数据类型,它由一组具名的常量组成。枚举常用于表示一组相关的离散值,赋予这些值易于理解的名称,增强代码的可读性和维护性。
1.特点
- 命名常量:枚举为一组常量提供了有意义的名字,使代码更具可读性。
- 整数值:在大多数编程语言中,枚举中的每个常量值都与一个整数值对应,通常从
0
开始递增,除非显式指定。- 类型安全:枚举类型可以帮助确保在代码中只能使用有效的枚举常量,而不会混用其他不相关的值。
2.语法
看个例子:
#include <stdio.h> enum Weekday { SUNDAY, // 默认值为 0 MONDAY, // 1 TUESDAY, // 2 WEDNESDAY, // 3 THURSDAY, // 4 FRIDAY, // 5 SATURDAY // 6 }; int main() { enum Weekday today; today = WEDNESDAY; if (today == WEDNESDAY) { printf("Today is Wednesday!\n"); } return 0; }
结果如图:
第二个例子(如果对某个成员赋值,后续成员会从该值开始递增):
enum EnumName { ENUM_VALUE1, //0 ENUM_VALUE2, //1 ENUM_VALUE3 = 10, // 可以显式指定值 ENUM_VALUE4 // 之后的值会递增,即11 };
枚举中的每个常量值都与一个整数值对应,通常从
0
开始递增,除非显式指定。
ps:要注意每个成员用","隔开
3.特殊情况
(一)枚举类型的隐式转换
枚举类型在 C 语言中实际上是整型,可能导致与整型混淆。比如,可以将整型值赋给枚举类型:
enum Weekday { SUNDAY, MONDAY, TUESDAY }; enum Weekday day = 3; // 这是合法的,但不安全
(二)枚举的作用域
在 C 语言中,枚举的常量在枚举定义的作用域内可见,但不会限定在枚举类型内部:
enum Colors { RED, GREEN, BLUE }; int color = RED; // 合法,因为 RED 在全局作用域中可见
(三)缺乏类型安全
尽管枚举在一定程度上提供了类型安全,但它们的整型本质仍然使得在比较或赋值时存在风险。例如,可以将一个枚举类型的变量赋值为与之不相关的整数值,这可能导致逻辑错误:
enum Direction { NORTH, SOUTH, EAST, WEST }; enum Direction dir = 10; // 合法,但不正确
(四)枚举的拓展
在 C 语言中,枚举类型一旦定义就不能被扩展或修改。它的值和成员是固定的。因此,枚举不能如同其他数据类型那样进行动态修改。
总结
到此这篇关于C语言联合体和枚举的文章就介绍到这了,更多相关C语言联合体和枚举内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!