在 C 语言和 C++ 中,__attribute__((packed))
是一种用于数据结构体的编译器扩展属性,这个属性主要用于修改结构体的内存对齐行为。
背景知识:结构体内存对齐
在许多计算机架构中,编译器会自动对数据进行对齐(alignment),也就是说,数据在内存中的地址会遵循一定的对齐规则。这是为了提升数据访问的效率,因为许多处理器在访问对齐的内存地址时速度更快。通常,结构体的每个成员会根据其数据类型按照某种对齐规则进行对齐,同时整个结构体也会有自然对齐值,以适应其最大成员类型的对齐要求。
对齐带来的问题 & 解决:
默认情况下,结构体中的每个成员会在内存中按照其对齐要求进行排列,这可能会导致在两个成员之间出现空闲的字节(称为填充字节),从而浪费一些内存空间。这种内存的浪费在嵌入式系统或者对内存占用敏感的应用场景中可能会被认为不可接受。
使用 __attribute__((packed))
的意义
__attribute__((packed))
是 GCC 编译器的一个扩展功能,用于要求编译器对指定的数据结构取消默认的对齐填充。- 有了
packed
属性,编译器会尽量紧密排列结构体的成员,将数据结构的大小减小到可能的最小值,即所有的字段按照声明的顺序紧密排列,而不插入任何额外的填充字节。
应用情境:
- 节省内存:在一些内存受限的环境中,例如某些嵌入式系统。
- 数据协议格式固定:结构体用于与特定格式的存储或通信协议(例如文件格式或网络数据包)进行交互,确保数据对齐和结构体内存布局一致性。
__attribute__((packed))
或 #pragma pack
会禁用对齐,紧凑排列结构体的成员。
以下是代码示例:
#include <stdio.h>#pragma pack(push, 1) // 设置为1字节对齐
struct packed_struct {char a; // 1字节int b; // 4字节short c; // 2字节
};
#pragma pack(pop) // 恢复默认对齐struct normal_struct {char a; // 1字节int b; // 4字节short c; // 2字节
};int main() {printf("Size of normal_struct: %zu bytes\n", sizeof(struct normal_struct));printf("Size of packed_struct: %zu bytes\n", sizeof(struct packed_struct));return 0;
}
代码解析
normal_struct
:- 系统会自动对齐结构体成员,默认对齐方式通常基于成员最大类型的大小(如
int
为4字节、short
为2字节)。 char a
占用1字节,但后面会填充3字节(以满足int
的对齐要求)。int b
正常对齐,占用4字节。short c
后面可能还会补齐额外字节(如果需要),使结构体总大小为int
的倍数。
- 系统会自动对齐结构体成员,默认对齐方式通常基于成员最大类型的大小(如
packed_struct
:- 禁用了默认对齐规则,结构体的成员按紧凑方式存放。
- 泄露的空间被完全消除,每个成员紧跟前一个成员存储。
运行结果(可能因平台不同而异)
假设在一个系统中:
int
是4字节,short
是2字节,char
是1字节。
运行结果可能如下:
Size of normal_struct: 12 bytes
Size of packed_struct: 7 bytes