10 结构体对齐与填充
1. 为什么 sizeof(struct) 大于成员之和
结构体成员可能因对齐要求而插入成员间填充;结构体末尾也可能补齐,以保证结构体数组的每个元素正确对齐。具体布局由平台 ABI、编译器及相关设置决定。
假设 char 为 1 字节,int 为 4 字节且要求 4 字节对齐:
struct A {
char a; // 偏移 0
int b; // 偏移 4(前有 3 字节填充)
char c; // 偏移 8(末尾再填充 3 字节)
}; // sizeof(struct A) == 12
struct B {
char a; // 偏移 0
char c; // 偏移 1
int b; // 偏移 4(前有 2 字节填充)
}; // sizeof(struct B) == 8
同样的成员,顺序不同,结构体大小可能不同。
2. sizeof 与 offsetof
#include <stddef.h>
sizeof(struct A); // 整个结构体的字节数:12
offsetof(struct A, b); // b 距离结构体起始处的字节偏移:4
offsetof(struct A, c); // 8
offsetof 用于检查成员偏移;不要把它误认为成员大小或绝对地址。
3. packed 的作用与风险
以下是 GCC/Clang 的编译器扩展写法,不是标准 C 关键字:
struct __attribute__((packed)) Packet {
char type; // 偏移 0
int value; // 偏移 1
}; // 假设 int 为 4 字节,通常占 5 字节
packed减少或消除通常的对齐填充;不会缩小int自身的大小。value可能处于未对齐地址;直接访问是否安全及效率如何依赖目标架构和编译器。- 蓝牙/TWS 二进制协议解析:
packed不负责处理字节序、长度校验或字段合法性;不要未经检查就把收到的数据缓冲区强制转换为结构体指针使用。按协议逐字段读取,或在验证长度和布局后谨慎使用memcpy,通常更可控。
一句话记忆:对齐决定成员放在哪里;填充影响结构体占多大;packed 改变布局,但不自动保证协议解析安全。