目录
一.联合体
1.联合体的声明
2.联合体的特点
(一)内存共享
(二)大小等于最大成员的大小
另一特殊情况:
(三)一次只能使用一个成员
3.联合体相比较于结构体
(一)内存分配
(二)使用方式
(三)内存布局对比
(四)总结
二.枚举
1.特点
2.语法
3.特殊情况
(一)枚举类型的隐式转换
(二)枚举的作用域
(三)缺乏类型安全
(四)枚举的拓展
一.联合体
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 语言中,枚举类型一旦定义就不能被扩展或修改。它的值和成员是固定的。因此,枚举不能如同其他数据类型那样进行动态修改。