蓝色大气企业网站phpcms模板,免费的个人简历电子版,接网站开发哪里好,dedecms 关闭网站1. 联合体
C语言中的联合体#xff08;Union#xff09;是一种数据结构#xff0c;它允许在同一内存位置存储不同类型的数据。不同于结构体#xff08;struct#xff09;#xff0c;结构体的成员各自占有独立的内存空间#xff0c;而联合体的所有成员共享同一块内存区域…1. 联合体
C语言中的联合体Union是一种数据结构它允许在同一内存位置存储不同类型的数据。不同于结构体struct结构体的成员各自占有独立的内存空间而联合体的所有成员共享同一块内存区域。这意味着在同一时间联合体中只能存储一个成员的值其他成员会被覆盖。
1.1 联合体的基本语法
联合体的声明与结构体相似使用关键字union来定义。
编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。所以联合体也叫共用体。
一个简单的联合体例子如下
#include stdio.hunion Data {int i;float f;char str[20];
};int main() {union Data data;data.i 10;printf(data.i %d\n, data.i);data.f 220.5;printf(data.f %.2f\n, data.f);// 注意data.i的值会被覆盖printf(data.i %d\n, data.i); // 这个值会发生变化return 0;
}1.2 联合体的内存分配与大小计算
在C语言中联合体的所有成员都共享同一块内存区域。当你定义一个联合体时它的内存空间并不会为每个成员分配独立的内存而是为所有成员分配一块共享的内存区域。这样联合体的内存大小至少等于成员中最大类型的成员大小。
举个例子
#include stdio.hunion Data {int i; // 4 字节float f; // 4 字节char str[20]; // 20 字节
};int main() {printf(Size of union Data: %lu\n, sizeof(union Data));return 0;
}解释
int i 通常占用 4 字节。float f 通常也占用 4 字节。char str[20] 占用 20 字节每个字符占 1 字节。
由于联合体的成员共享内存它的大小等于其中最大成员的大小。在这个例子中char str[20] 的大小是 20 字节因此联合体的大小会是 20 字节。换句话说联合体的内存分配通常是由它的最大成员决定的且内存中只能保存一个成员的数据。
输出
Size of union Data: 201.2.1 联合体的内存对齐
除了最大成员的大小外还要注意内存对齐memory alignment。C语言中对于每个数据类型通常都有对齐要求。具体对齐方式与系统架构、编译器有关在C语言第17节自定义类型——结构体已经讲过了点击链接即可查看。内存对齐的目的是为了提高访问效率因此编译器往往会将数据类型按一定的字节边界对齐例如4字节对齐、8字节对齐等。
内存对齐的示例
假设我们使用的是32位或64位的架构它可能要求对齐到4字节边界。我们来观察一下一个包含不同类型成员的联合体的内存分配。
// VS2022 MSVC
#include stdio.hunion Example {char c; // 1 字节int i; // 4 字节double d; // 8 字节
};int main() {printf(Size of union Example: %lu\n, sizeof(union Example));return 0;
}解释
char c 占用 1 字节。int i 占用 4 字节。double d 占用 8 字节。
但由于内存对齐的原因联合体的实际大小可能会比这些单独成员的大小之和要大。通常为了提高访问速度编译器会插入一些填充字节padding使得联合体的内存大小是最大对齐数的倍数。
输出
Size of union Example: 8为什么是8字节呢因为联合体中最大成员是double d它的大小是8字节而且对齐数也是8。因此整个联合体的大小会是8字节。
1.2.2 联合体内存分配的详细说明
成员共享内存 联合体中的所有成员共享同一块内存区域。在任何时刻联合体的内存中只会保存一个成员的值。联合体的大小通常由最大成员的大小决定因为它必须能够容纳最大成员的数据。 内存对齐 编译器为了提高数据访问的效率会根据平台的对齐要求插入填充字节padding。内存对齐确保数据按适当的字节边界存放例如4字节对齐、8字节对齐从而使得CPU可以更快速地访问这些数据。在一些平台上数据类型可能有特定的对齐要求。 联合体的大小计算 联合体的大小通常等于其最大成员的大小但是为了满足内存对齐的要求联合体的实际大小可能会大于最大成员的大小。它会被填充到最接近对齐要求的倍数。内存对齐填充通常是由编译器自动管理的但了解这一点对于理解联合体的内存分配非常重要。
1.2.3 进一步的例子多成员联合体与内存对齐
假设我们有一个更复杂的联合体其中包含不同类型的数据并且考虑到内存对齐的影响
#include stdio.hunion Complex {char c[21]; // 21 字节int i; // 4 字节double d; // 8 字节short s; // 2 字节
};int main() {printf(Size of union Complex: %lu\n, sizeof(union Complex));return 0;
}分析
char c[21]这是一个字符数组它占用21字节每个字符占1字节。没有对齐要求。int i整数类型通常占用4字节。要求 4 字节对齐即它会被存储在4字节对齐的位置。double d双精度浮点类型通常占用8字节。要求 8 字节对齐因此它会被存储在8字节对齐的位置。short s短整型通常占用2字节。要求 2 字节对齐它会被存储在2字节对齐的位置。
联合体的实际大小
联合体的大小由最大对齐数决定。在这个例子中最大成员是 double d虽然 char c[21] 占用了 21 字节但是其对齐数取决于存储的元素即其对齐数为 char 的对齐数1它的大小是 8 字节其对齐数为 8 。因此联合体的大小将是 8 字节的倍数。即使 char c[21] 占用了 21 字节但联合体的总大小会由于对齐要求被填充到适合最大成员对齐的大小。
因此联合体的实际大小会是 24 字节。 这是因为
double d 会占用 8 字节并且需要 8 字节对齐因此联合体的总大小将被填充到最接近8字节对齐的倍数即 24 字节。
1.2.4 总结
联合的大小至少是最大成员的大小。当最大成员大小不是最大对齐数的整数倍的时候就要对齐到最大对齐数的整数倍。
1.3 联合体的特点 共享内存联合体的所有成员共享同一块内存区域因此一个联合变量的大小至少是最大成员的大小。 例子 #include stdio.hunion Data {int i;float f;char str[20];
};int main() {union Data data;printf(data: %p\n, data);printf(data.i: %p\n, data.i);printf(data.f: %p\n, data.f);printf(data.str: %p\n, data.str);return 0;
}只能存储一个成员的值每次只能访问联合体中的一个成员。给一个成员赋值时其他成员的值会被覆盖。 节省内存空间联合体在节省内存方面非常有用尤其是当你需要存储多种不同类型的数据但在任何时刻只需要其中一个类型的数据时。 例子 #include stdio.h
#include string.h
union Data {int i;float f;char str[20];
};int main() {union Data data;strcpy(data.str, abcdefgh);data.i 0x11223344;return 0;
}1.4 访问联合体成员
联合体成员可以通过.点操作符访问。例如
union Data data;
data.i 100;
printf(data.i %d\n, data.i); // 输出100data.f 98.6;
printf(data.f %.2f\n, data.f); // 输出98.61.5 联合体的使用场景
1.5.1 场景①
在网络通信中我们经常需要处理不同类型的数据包。这些数据包的内容可能会根据协议的不同而有所不同。例如有些协议可能传输整数数据有些可能传输浮动数数据还有些可能传输字符串数据。在这种情况下我们可以使用联合体来处理这些不同的数据格式。
场景描述
假设你正在开发一个通信协议处理程序该程序需要解析网络传输过来的数据包。每个数据包的类型和内容可能会有所不同但在任何时刻每个数据包只会包含一种类型的数据。为了节省内存你可以使用联合体来存储不同类型的数据。
协议A 可能会传输一个 整数比如用户ID。协议B 可能会传输一个 浮动数比如温度传感器的数据。协议C 可能会传输一个 字符串比如设备状态信息。
通过联合体你可以为这些数据包定义一个结构使得它们共享同一块内存。每次你收到一个数据包时根据协议类型你可以决定是存储整数、浮动数还是字符串但内存中始终只有一种数据。
优势
节省内存由于不同数据类型共享内存只有在需要时才会使用最大类型的内存例如字符串可能占用更多的字节。灵活性能够处理不同协议的数据格式只需要用一个联合体存储数据。
这个场景在 网络通信、嵌入式系统、文件格式解析 等领域非常常见特别是在需要处理多种类型数据的系统中。
1.5.2 场景②
姓名性别年龄婚姻状况婚姻状况标记未婚已婚离婚结婚日期配偶姓名子女数量离婚日期子女数量
struct Person // 定义职工个人信息结构体类型
{char name[20]; // 姓名char sex; // 性别int age; // 年龄union MaritalState marital; // 婚姻状况int marryFlag; // 婚姻状况标记
};union MaritalState // 定义婚姻情况共用体
{int single; // 未婚struct MarriedState married; // 已婚struct DivorceState divorce; // 离婚
};struct MarriedState // 定义已婚结构体类型
{struct Date marryDay; // 结婚日期char spouseName[20]; // 配偶姓名int child; // 子女数量
};struct DivorceState // 定义离婚结构体类型
{struct Date divorceDay; // 离婚日期int child; // 子女数量
};struct Date
{int year;short month;short day;
};1.6 联合体判断大小端
int check_sys()
{union{int i;char c;}un;un.i 1;return un.c;//返回1是小端返回0是大端
}在C语言第16节数据在内存中的存储已经讲过这里不再赘述。
2. 枚举类型
C语言中的枚举类型enum是一种用户自定义的数据类型用于表示一组具名的常量。枚举类型将一组相关的常量组合在一起并赋予它们有意义的名字。使用枚举可以使程序的代码更加清晰、易于理解和维护。 一周的星期一到星期日是有限的7天可以一 一列举 性别有男、女、保密也可以一 一列举 月份有12个月也可以一 一列举 三原色也是可以一 一列举 2.1 枚举类型的基本语法
定义枚举类型的语法如下
enum 枚举名 {常量1 值1,常量2 值2,常量3 值3,...
};枚举名 是枚举类型的名称。常量 是枚举中的各个值通常是一些具名常量默认情况下枚举常量从 0 开始依次递增除非你为它们指定了不同的值。
2.1.1 例子基本枚举类型
#include stdio.henum Day {Sunday, // 默认值为 0Monday, // 默认值为 1Tuesday, // 默认值为 2Wednesday, // 默认值为 3Thursday, // 默认值为 4Friday, // 默认值为 5Saturday // 默认值为 6
};int main() {enum Day today;today Wednesday; // today 被赋值为 3printf(Today is day number: %d\n, today); // 输出Today is day number: 3return 0;
}2.2 枚举常量的默认值
如果你没有为枚举常量指定值默认情况下第一个常量的值为 0后续常量的值依次递增 1。例如在上面的代码中Sunday 默认值为 0Monday 为 1依此类推。
2.3 枚举常量的自定义值
你可以手动为枚举常量指定值。这意味着你可以设置任意值而不依赖于默认的递增规则。
#include stdio.henum Day {Sunday 1, // 设定为 1Monday 2, // 设定为 2Tuesday 5, // 设定为 5Wednesday 7, // 设定为 7Thursday, // 默认递增从 8 开始Friday, // 9Saturday // 10
};int main() {enum Day today;today Thursday; // today 被赋值为 8printf(Today is day number: %d\n, today); // 输出Today is day number: 8return 0;
}在这个例子中Sunday 和 Monday 的值分别被设置为 1 和 2而其他常量则从 Wednesday 开始自动递增。
2.4 枚举类型的实际应用
枚举常常用于表示一些固定的状态或选项比如星期几、颜色、方向、状态码等。它能够使代码更加清晰减少硬编码的数字。
2.4.1 例子使用枚举表示交通信号灯的状态
#include stdio.henum TrafficLight {Red, // 0Yellow, // 1Green // 2
};int main() {enum TrafficLight signal;signal Green;if (signal Green) {printf(Go!\n);} else if (signal Yellow) {printf(Caution!\n);} else {printf(Stop!\n);}return 0;
}在这个例子中TrafficLight 枚举表示了交通信号灯的三个状态红灯、黄灯和绿灯。每个状态有一个默认的整数值Red 为 0Yellow 为 1Green 为 2。通过这种方式代码更易理解避免了使用数字来表示信号灯的状态。
2.5 枚举类型的大小
在 C 语言中枚举类型的大小与编译器的实现有关。通常编译器会根据枚举常量的取值范围来决定枚举类型的存储大小。如果枚举的值在 int 类型的范围内编译器通常会选择 int 类型来存储枚举值。但有些编译器可能根据需要进行优化使用较小的存储类型。
可以使用 sizeof 来查看枚举类型的大小
#include stdio.henum Color {Red 1,Green,Blue
};int main() {printf(Size of enum Color: %lu\n, sizeof(enum Color)); // 输出枚举类型的大小return 0;
}2.6 枚举类型的转换
枚举常量本质上是整数因此可以将它们转换为整数类型或者将整数值赋给枚举变量。但要注意这种做法可能会导致不符合预期的结果。
#include stdio.henum Day {Sunday 1,Monday 2,Tuesday 3
};int main() {enum Day today;today 2; // 可以将整数赋给枚举变量printf(Today is day number: %d\n, today); // 输出Today is day number: 2return 0;
}拿整数给枚举变量赋值在C语言中是可以的但是在C是不行的C的类型检查比较严格。
2.7 枚举的优势
提高代码可读性枚举常量有具名的标识符使代码更具语义。减少硬编码数字避免了在代码中使用没有含义的数字常量。防止非法值枚举确保变量只能取定义好的值。简化调试具名常量方便在调试时辨识。增强维护性修改枚举值时只需要更改枚举定义无需修改多个代码位置。与 switch 语句结合使用枚举常量使得 switch 语句的条件判断更加清晰。支持位域与位运算结合使用可以用来表示多个标志位。
—完—