数据结构填充 - 2023.2 简体中文

Vitis 高层次综合用户指南 (UG1399)

Document ID
UG1399
Release Date
2023-12-18
Version
2023.2 简体中文

数据对齐 中的表格所示,本地数据类型具有精心定义的对齐结构,但用户定义的数据类型的对齐结构又如何呢?C++ 编译器也需要确保结构体或类中的所有成员变量都正确对齐。为此,编译器可在成员变量之间插入填充字节。此外,为了确保用户定义的类型的阵列中的每个元素都对齐,编译器可以在最后一个数据成员之后添加额外的填充。请考量以下示例:

表 1. 结构体对齐
struct One
{                           
    short int s;
    int i;
    char c;
}
struct Two
{
   int i;
   char c;
   short int s;
}

GCC 编译器始终假定 struct One 的实例的起始地址对齐到所有结构体成员的要求最严格的对齐地址处,在此例中,即 int。实际上,用户定义的类型的对齐要求正是以此方式计算所得的。假定存储器为 x86-64 对齐,其中 short int 的对齐为 2,int 的对齐为 4,为了使 struct Onei 数据成员得以适当对齐,编译器需要在 si 之间插入 2 个额外的填充字节以创建对齐,如下图所示。同样,为了对齐数据成员 c,编译器需要在 c 之后插入 3 字节。

对于 struct One,编译器将基于结构体元素的对齐方式推断总计大小为 12 字节。但如果结构体的元素已重新排序(如 struct Two 中所示),那么编译器现在即可推断得到较小的大小,即 8 字节。

图 1. 结构体填充

默认情况下,C/C++ 编译器将按结构体成员的声明顺序来完成这些成员的布局,在成员间或者最后一个成员之后可能按需插入填充字节,以确保每个成员都能正确对齐。但是,GCC C/C++ 编译器会提供语言扩展 __attribute__((packed)),以告知编译器不插入填充,而是改为允许结构体成员间不对齐。例如,如果系统正常情况下要求所有 int 对象都采用 4 字节对齐,那么使用 __attribute__((packed)) 可能导致将 int 结构体成员分配至错误偏移处。

您必须审慎考量 __attribute__((packed)) 的使用,因为访问未对齐的存储器可能导致编译器插入代码以逐个字节读取存储器,而不是一次性读取多个存储器区块。