1、结构体填充(Padding)
结构体填充是编译器在结构体中插入额外的字节(填充字节)以确保结构体的成员变量能够对齐到它们的地址边界。这样做可以提高内存访问的速度,因为许多CPU在访问特定对齐边界的内存时速度更快。结构体中的每个成员变量通常会对齐到其自身大小的倍数边界上。例如,一个 int 类型(4字节)通常对齐到4字节边界。结构体的大小通常会对齐到其最大成员大小的倍数上。
#include <stdio.h>
struct Example {
char a; // 1字节
int b; // 4字节
char c; // 1字节
};
int main() {
printf("Size of struct: %zu\n", sizeof(struct Example));
return 0;
}
2、结构体打包(Packing)
结构体打包指的是通过禁用或减小填充,以最小化结构体的内存占用。打包在某些情况下可以节省内存,但可能导致不对齐的数据访问,从而降低访问速度甚至导致访问错误。在许多编译器中,可以使用编译指令或编译器特定的 pragma
来启用结构体打包。
#include <stdio.h>
#pragma pack(1) // 设置结构体打包为1字节对齐
struct PackedExample {
char a; // 1字节
int b; // 4字节
char c; // 1字节
};
#pragma pack() // 还原默认对齐方式
int main() {
printf("Size of packed struct: %zu\n", sizeof(struct PackedExample));
return 0;
}
3、结构体填充与打包的对比
结构体的填充(Padding)和打包(Packing)各有优缺点,根据不同的应用需求选择适当的方式非常重要。结构体填充是为了满足CPU的对齐要求,编译器会在结构体成员之间加入额外的填充字节,以保证每个成员的内存地址都符合对齐要求。这种方式通常会增大结构体的整体大小,但有助于提高访问速度。结构体打包通过取消或减少填充字节,结构体可以节省内存,但这可能会导致不对齐访问,进而影响性能,特别是在不支持不对齐访问的硬件上。通常在与外部系统数据结构对接时使用,例如文件结构或网络协议解析。
特性 | 结构体填充(Padding) | 结构体打包(Packing) |
内存对齐 | 是,为了提高访问速度 | 否,尽量节省内存, 可能导致不对齐的访问 |
内存占用 | 通常更大,包含填充字节 | 更小,没有填充字节 |
访问速度 | 更快,符合CPU的对齐要求 | 可能较慢, 尤其在不支持不对齐访问的CPU上 |
使用场景 | 大多数情况, 特别是在对速度有要求时 | 内存受限的情况或 与外部系统数据结构对接时 |
#include <stdio.h>
#include <stdint.h>
// 定义结构体,使用默认的填充
struct PaddedStruct {
char a; // 1 字节
int b; // 4 字节
short c; // 2 字节
};
// 使用 pragma 指令取消填充,进行打包
#pragma pack(push, 1)
struct PackedStruct {
char a; // 1 字节
int b; // 4 字节
short c; // 2 字节
};
#pragma pack(pop)
int main() {
// 打印结构体大小,查看填充和打包的差异
printf("Size of PaddedStruct: %zu bytes\n", sizeof(struct PaddedStruct));
printf("Size of PackedStruct: %zu bytes\n", sizeof(struct PackedStruct));
// 创建结构体变量并访问其成员
struct PaddedStruct padded = {'A', 100, 50};
struct PackedStruct packed = {'A', 100, 50};
printf("\nPaddedStruct:\n");
printf("a: %c, b: %d, c: %d\n", padded.a, padded.b, padded.c);
printf("\nPackedStruct:\n");
printf("a: %c, b: %d, c: %d\n", packed.a, packed.b, packed.c);
return 0;
}
4、使用场景
结构体填充适合大多数情况,特别是对性能要求较高的场景,因为内存对齐能显著提高访问速度。
#include <stdio.h>
// 使用默认的结构体填充
struct PaddedStruct {
char a; // 1 字节
int b; // 4 字节
short c; // 2 字节
};
int main() {
struct PaddedStruct padded = {'A', 100, 50};
// 打印结构体大小和成员变量的地址,查看内存对齐
printf("Size of PaddedStruct: %zu bytes\n", sizeof(struct PaddedStruct));
printf("Address of a: %p\n", (void*)&padded.a);
printf("Address of b: %p\n", (void*)&padded.b);
printf("Address of c: %p\n", (void*)&padded.c);
return 0;
}
结构体打包常用于与外部系统或硬件对接的数据结构,例如网络协议数据包结构、文件格式解析等。这些场景中,节省内存比访问速度更重要。
#include <stdio.h>
#include <stdint.h>
// 使用 #pragma pack 指令进行打包,取消填充
#pragma pack(push, 1)
struct PackedStruct {
char a; // 1 字节
int b; // 4 字节
short c; // 2 字节
};
#pragma pack(pop)
int main() {
struct PackedStruct packed = {'A', 100, 50};
// 打印结构体大小和成员变量的地址,查看打包后的内存布局
printf("Size of PackedStruct: %zu bytes\n", sizeof(struct PackedStruct));
printf("Address of a: %p\n", (void*)&packed.a);
printf("Address of b: %p\n", (void*)&packed.b);
printf("Address of c: %p\n", (void*)&packed.c);
return 0;
}