本章内容
结构体
结构体类型的声明
结构的自引用
结构体变量的定义和初始化
结构体传参
结构体实现位段(位段的填充&可移植性)
枚举
枚举类型的定义
枚举的优点
枚举的使用
联合
联合类型的定义
联合的特点
联合大小的计算
结构体
数组是存放同类型数据的集合。那么结构体就可以理解为能够存放不同类型数据的集合。结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
结构体类型的声明
以下是一个结构体类型的声明:
struct student { char name[20]; int age; int phone[20]; };
这样我们就实现了一个student的结构体类型声明。
结构体的自引用
在 C 语言中,结构体可以包含指向自身类型的指针,从而实现自引用。这在创建包含递归或链表结构的数据类型时十分有用。
#include <stdio.h> // 定义包含自引用的结构体 struct Node { int data; struct Node* next; }; int main() { // 创建结构体变量 struct Node n1, n2, n3; // 设置结构体的数据和 next 指针 n1.data = 10; n2.data = 20; n3.data = 30; n1.next = &n2; n2.next = &n3; n3.next = NULL; // 遍历链表并输出数据 struct Node* current = &n1; while (current != NULL) { printf("Data: %d\n", current->data); current = current->next; } return 0; }
在上面的示例中,我们定义了一个包含自引用的结构体 Node,其中包含一个整数类型的 data 成员以及一个指向 Node 类型的 next 指针。
在 main 函数中,我们创建了三个结构体变量 n1、n2 和 n3,并设置它们的 data 成员为不同的值。然后,我们使用 & 运算符将 next 指针指向下一个结构体变量,最后一个结构体变量的 next 指针设置为 NULL。
通过遍历 next 指针,我们可以沿着链表遍历所有结构体,并将每个结构体的 data 值输出到控制台。
这样的结构体自引用在构建树、链表和其他递归数据结构时非常常见和有用。
结构体变量的定义和初始化
对于这个,我们直接通过代码来更好的理解
struct Point { int x; int y; }p1; //声明类型的同时定义变量p1 struct Point p2; //定义结构体变量p2 //初始化:定义变量的同时赋初值。 struct Point p3 = { 3, 4 }; struct Stu { char name[15]; int age; }; struct Node { int data; struct Point p; struct Node* next; }n1 = { 10, {4,5}, NULL }; //结构体嵌套初始化 int main() { struct Node n2 = { 20, {5, 6}, NULL };//结构体嵌套初始化 struct Stu s = { "zhangsan", 20 };//初始化 return 0; }
这里我们直接通过调试找到监视窗口来更好的查看。
在这里稍微举一个例子,如果大家觉得定义类型过长可以通过typedef来改变。
struct student { char name[20]; int age; int phone[20]; }; typedef struct student Stu; int main() { Stu zhangsan = { "zhangsan",20,12345677 }; return 0; }
直接定义为Stu,我们就可以直接使用了。
结构体内存对齐
我们已经掌握了结构体的基本使用了。
现在我们深入讨论一个问题:计算结构体的大小。
是一个非常热门的面试考点:
结构体内存对齐
我们看这样一段代码:
struct S1 { char c1; int i; char c2; }; int main() { printf("%d\n", sizeof(struct S1)); return 0; }
也许会认为是6对吗?那我们来看一下运行结果吧。
如果我们把c2和i的位置换一下呢?我们直接看运行截图
为什么我们只是更改了一下顺序就发生了这样的情况,是因为结构体会以提升运行效率为目的从而浪费一些空间内存。
那么接下来我们来了解一下,结构体对齐规则
首先得掌握结构体的对齐规则:
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8
- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
offsetof
那么接下来我们要知道一个东西,叫做offsetof 是一个 C 语言标准库宏,用于获取结构体中成员的偏移量(即成员相对于结构体起始地址的字节偏移量)。offsetof 宏通常与指针运算符及结构体类型一起使用。
那么我们我们直接看结构体成员中的偏移量。
那么我们来根据规则计算一下
是8个大小,刚好是最大默认对齐数的两倍,所以他的大小就是8
接下来来计算它的大小
这样呢大小是9,必须是最大默认对齐数的整数倍也就是4的整数倍,那就是12了。