自定义数据类型
结构体(字段)
结构体的声明方法
一般的结构体声明格式
struct tag { member - list; }variable - list;注:在创建了sturct tag的变量类型后,还会附带创建variable-list中的变量为此类型的全局变量
声明匿名结构体
struct { member - list; }variable-list;注:
- 因为类型没有名字,所以匿名结构体类型只能在variable-list中创建
- 两个成员都相同的结构体的情况下,编译器也会把他们当成不同的结构体类型
匿名结构体的定义可以这样写:
struct { int a; char b; float c; } arr[20], *ptr;
结构体嵌套结构体
写法
struct node { int data; struct node* next; };错误写法
struct Node { int data; struct Node next; // 此处错误 };
结构体的内存对齐
内存对齐的意义
- 跨平台:不是所有的硬件平台都能访问任意地址上的任意数据的。某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
- 性能优化:为了访问未对齐的内存,处理器需要作两次内存访问。而对齐的内存访问仅需要一次访问
- 总的来说,就是空间换时间
结构体内存对齐规则
基本对齐单位:每个成员有自身对齐值(通常为类型大小,如 char=1、int=4)
默认对齐数:编译器有默认对齐系数(如 VS 默认 8,GCC 默认 4),成员实际对齐值取「自身类型大小」与「默认对齐数」的较小值
优先级:当指定
#pragma pack (n)时,默认对齐数被 n 替代,成员实际对齐值取「类型大小」与「n」的较小值结构体整体对齐值为成员最大对齐值
整体对齐调整:结构体最终总大小是「所有成员实际对齐值的最大值」的整数倍,不足则填充
成员存放规则:每个成员起始地址(偏移量)需是自身对齐值的整数倍(含0),不足则填充字节
注:第一个成员的偏移量定义为0
整体对齐规则:结构体总大小需是整体对齐值的整数倍,不足则在末尾填充
嵌套结构体:内部结构体的对齐值为其自身最大成员对齐值,整体仍遵循上述规则
补充
“不足则填充” 是内存对齐中为满足对齐规则而插入空白字节的过程,可从两个层面理解
成员间填充 当某个成员的起始地址不符合其对齐要求时,在该成员前插入空白字节(填充),直到地址满足 “是自身对齐值整数倍” 的条件
例:
struct { char a; int b; }中,char 占 1 字节,int 对齐值为 4。a 存储在地址 0 后,b 需从地址 4 开始,因此在 a 和 b 之间填充 3 个空白字节(地址 1-3)结构体末尾填充 结构体总大小需是整体对齐值(所有成员对齐值的最大值)的整数倍。若计算的总大小不满足,在结构体末尾补充空白字节
例:上述结构体中,成员总大小为 1(a)+3(填充)+4(b)=8,恰好是整体对齐值 4 的 2 倍,无需末尾填充;若成员为
char a; short b(对齐值 2),总大小 1+1(填充)+2=4,满足 2 的整数倍要求填充的空白字节不存储有效数据,仅用于保证内存对齐,以提升 CPU 访问效率
位段
概念
位段是基于结构体的空间优化数据类型,核心作用是节省内存空间,常见于底层开发(如网络开发、硬件驱动),通过指定成员占用的二进制位数,减少普通结构体因内存对齐产生的空间浪费
位段与结构体差异
成员类型限制
位段成员必须是整型相关类型(
int、unsigned int、signed int,实际测试中char等整型也可)位数指定规则
成员名后需紧跟冒号(
:)和数字,数字表示该成员占用的二进制位数分配逻辑
即使总位数未超过单个
int的 32 位,若后续成员无法容纳,仍会开辟新的int空间(如_d需要 30 位,第一个空间仅剩 15 位,无法容纳,因此开辟第二个 32 位空间)
成员存储细节
- 成员按声明顺序依次存储在开辟的空间中
- 若当前空间剩余位数不足以容纳下一个成员,是否利用剩余空间无 C 标准定义(取决于编译器)
- 成员在内存中的存储方向(从左到右 / 从右到左)无标准定义,属于编译器相关行为
声明示例
struct A { int _a : 2; // _a 占用 2 个二进制位 int _b : 5; // _b 占用 5 个二进制位 int _c : 10; // _c 占用 10 个二进制位 int _d : 30; // _d 占用 30 个二进制位 }; // 上述位段总占用位数:2 + 5 + 10 + 30 = 47 bit(约 6 字节)位段的不确定性
- int位段的符号性:
int位段被当作有符号还是无符号整数,标准未定义 - 最大位数限制:不同平台(16 位 / 32 位 / 64
位)对整型位数限制不同,例如 16 位机器上,位段成员指定 27 bit 会超出
int(16 bit)范围,导致错误 - 剩余空间利用:前一个成员存储后剩余的空间,是否用于存储下一个成员,无标准定义
- 存储方向:成员在内存中按左到右还是右到左分配,无标准定义
- int位段的符号性: