一、结构体
1. 概念
结构体是一种自定义的数据类型,它是一些值的集合,这些值称为成员变量,结构体由多个数据成员组成,这些成员可以是不同类型的变量。
2. 结构体的声明
2.1 一般声明
struct student {
char name[10];
int age;
};
2.2 特殊声明
//匿名结构体类型
struct {
char name[10];
int age;
}s1,s2;
匿名结构体类型,只能在声明结构体类型时,顺带创建其结构体变量,其余地方无法再创建该类型的变量。也就是只能使用一次。
注意:
即使多个匿名结构体的成员完全一致,它们也属于不同的结构体类型。
3. 结构体的自引用
在结构体中包含一个类型为该结构体本身的成员是否可以?
struct ListNode {
int val;
struct ListNode next;
};
注意:这种自引用方式是不可以的,结构体的成员不能是自身类型,因为编译器无法确定结构体的大小。
正确的自引用方式:
struct ListNode {
int val;
//使用指针来保存自身类型成员的地址
struct ListNode* next;
};
4. 结构体变量的定义和初始化
struct ListNode {
int val;
struct ListNode* next;
}n1, n2;//声明类型的同时定义变量n1,n2
//定义变量同时赋初始值
struct ListNode n3 = { 10, NULL };
5. 结构体内存对齐
5.1 结构体内存对齐规则★
(1)第一个成员在与结构体变量偏移量为0的地址处;
(2)其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处;
对齐数 = min(编译器默认对齐数, 该成员变量大小)
(3)结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍;
(4)如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
5.2 为什么存在内存对齐?
(1)平台原因
不是所有平台的硬件都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取出某些特定类型的数据。
(2)性能原因
数据结构(尤其是栈)应该尽可能的在自然边界上对齐;因为为了访问未对齐的内存,处理器需要两次内存访问,而对于对齐的内存访问只需要一次访问。
(3)总结
本质是因为硬件平台限制,导致数据读取可能会出现低效率问题,通过“浪费”一些空间的方式,来达到快速读取目标数据的目的。
5.3 修改默认对齐数
#pragma pack(4)//修改默认对齐数为4
struct Node {
char ch;
int a;
char cr;
};
#pragma pack(8)//修改默认对齐数为8
二、位段
1. 概念
位段是C语言中的一个特性,用于在结构体中以位为单位定义和访问数据。它允许我们对结构体成员进行精确的位级别的控制,以节省内存空间。
(1)位段成员只能是整型类型(包括有符号和无符号整数类型)或枚举类型,不能使用浮点类型、结构体类型等非整型类型;
(2)位段的成员名后有一个冒号和数字。
struct A {
int a : 2;//表示只需要2个bit
int b : 5;
int c : 10;
};
2. 位段的内存分配
(1)位段在空间上是按照需要以4字节或1字节的方式来开辟的;
(2)位段涉及很多不确定因素,是不支持平台的,若是注直可移植性的程序应该避免使用位段。
3. 位段的跨平台问题
(1)int位段被当成有符号数还是无符号数是不确定的;
(2)位段中最大位的数目不能确定(比如在16位机器中最大为16,32位机器最大为32);
(3)位段中的成员在内存中是从左向右分配,还是从右向左分配,标志尚未定义;
(4)当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余位时,是舍弃剩余位还是利用,这是不确定的。
4. 位段的应用
多用于计算机网络中一些协议首部结构,比如:
三、枚举
1. 枚举类型的定义
enum day {
mon, // 0
tues, // 1
wed, // 2
thur, // 3
fri, // 4
sat, // 5
sun // 6
};
{}中的内容是枚举类型的可能取值,也叫枚举常量。取值默认从0开始,依次递增1,如果在定义的时候赋予了初始值,则后续在此基础上依次加一。
enum day {
mon, // 0
tues = 2, // 2
wed, // 3
thur, // 4
fri = 8, // 8
sat, // 9
sun // 10
};
2. 枚举的优点
(1)增加代码的可读性和可维护性;
(2)相比较于#define定义的标识符,枚举有类型检查,更加严谨;
(3)防止命名污染;
(4)便于调试;
(5)使用方便,一次可以定义多个常量。
四、联合(共用体)
1. 联合类型的定义
联合是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特点是这些成员共用同一块空间,所以联合也叫共用体。
union re {
int a;
char ch;
};
2. 联合的特点
联合的所有成员共用同一块内存空间,一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
3. 联合大小计算
(1)联合大小至少是最大成员的大小;
(2)当最大成员大小不是最大对齐数的整数倍的时候,就需要对齐到最大对齐数的整数倍。
比如:
union {
char arr[5];
int i;
};
该联合最大成员大小为5,最大对齐数是4,所以需要对齐到最大对齐数整数倍数,即该联合大小为8。