1、总结:
结构体大小的计算:
1. 结构体中成员变量的大小对齐
2. 结构体的总大小为各成员变量大小的总和,加上结构体头的大小
3. 结构体头的大小由编译器决定,一般为1或2个字节,但也可能是4或8个字节
4. 结构体中各成员变量的大小由编译器决定,一般为1、2、4、8个字节,但也可能是16、32、64个字节
5. 结构体中各成员变量的大小对齐,以结构体中最大的成员变量大小为基准
关键点 说明
成员对齐 每个成员的起始地址必须是其类型大小的整数倍。
整体对齐 结构体总大小必须是最大成员对齐值的整数倍。
填充字节 编译器自动插入不可见的填充字节以满足对齐规则。
优化手段 调整成员顺序、使用 #pragma pack 或 alignas、显式填充。
注意事项 强制修改对齐可能导致性能下降或平台兼容性问题,慎用 #pragma pack(1)。
公式:
结构体大小 = Σ(成员大小 + 填充字节) + 末尾填充(若需要)
填充字节 = (对齐值 - (当前偏移 % 对齐值)) % 对齐值
基本数据类型占用内存大小(平台差异,可用sizeof()确定变量占用内存的大小):
数据类型 典型大小(字节) 对齐值(字节)
char 1 1
short 2 2
int 4 4
float 4 4
double 8 8
指针(32位) 4 4
指针(64位) 8 8
2、举例:
#include <stdalign.h>
#include <stdio.h>
// 示例结构体
typedef struct {
char a; // 1 字节
int b; // 4 字节
double c; // 8 字节
} MyStruct;
int main() {
/*
计算过程:
char a 占 1 字节。
int b 需要起始地址为 4 的倍数,因此在 a 后填充 3 字节。
int b 占 4 字节。
double c 起始地址为 8(已对齐),占 8 字节。
总大小 = 1 + 3(填充) + 4 + 8 = 16 字节。
结构体整体需要对齐到 double 的大小(8 字节),16 是 8 的整数倍,无需额外填充。
*/
printf("结构体大小:%zu 字节\n", sizeof(MyStruct)); // 输出:16 字节(对齐后的结果)
// 修改对齐方式
// 1、#pragma pack(1) // 强制 1 字节对齐
#pragma pack(1) // 1 字节对齐(无填充)
struct Packed {
char a; // 1
int b; // 4
double c; // 8
}; // 总大小 = 1 + 4 + 8 = 13
printf("结构体大小:%zu 字节\n", sizeof(struct Packed)); // 输出:13 字节(无填充)
#pragma pack() // 恢复默认对齐
/* 2. 使用 alignas 关键字(C11 / C++11)
需要使用MSVC扩展语法,在结构体定义前加上 __declspec(align(...)),否则会报错
C++不需要添加stdalign.h头文件
*/
struct Aligned {
char a;
__declspec(align(8)) int b; // 强制 b 对齐到 8 字节
double c;
}; // 总大小 = 1 + 7(填充) + 4 + 0 + 8 = 20(需对齐到 8 的倍数 → 24)
printf("结构体大小:%zu 字节\n", sizeof(struct Aligned)); // 输出:24 字节(对齐后的结果)
return 0;
}
扩展:控制内存对齐alignas、_Alignas、#pragma pack(1)、__declspec(align(8))区别:
对比项 | alignas /_Alignas | #pragma pack(n) | __declspec(align(n)) |
标准支持 | C11/C++11 标准 | 非标准(编译器扩展) | 非标准(MSVC 扩展) |
跨平台性 | ✔️(GCC/Clang/MSVC) | ✔️(所有编译器支持类似语法) | ❌(仅 MSVC) |
作用目标 | 变量、类型、结构体成员 | 结构体/联合体整体对齐 | 变量、类型、结构体成员 |
对齐值要求 | 2 的幂次,且 ≥ 自然对齐值 | 任意正整数(通常 1/2/4/8/16) | 2 的幂次,且 ≥ 自然对齐值 |
典型场景 | SIMD 指令、硬件寄存器映射 | 网络协议、文件格式解析 | Windows 高性能计算 |
内存布局影响 | 精确控制对齐,可能增加填充 | 减少填充,可能导致未对齐访问 | 精确控制对齐,可能增加填充 |