C结构元素对齐(ansi)

C struct elements alignment (ansi)

本文关键字:ansi 对齐 结构 元素      更新时间:2023-10-16

只是一个简单的问题。。。标准对结构构件对齐有何规定?例如这个:

struct
{
uint8_t a;
uint8_t b;
/* other members */
} test;

是否保证b位于结构起点的偏移量1处?感谢

标准(从C99开始)并没有真正说明什么。

唯一真正的保证是(void *)&test == (void *)&aa位于比b更低的地址。其他一切都取决于实施。

C11 6.7.2.1结构和并集说明符p14表示

结构或并集对象的每个非位字段成员都在适用于其类型的实现定义的方式。

意味着您不能对ab的地址之间的差异进行任何可移植的假设。

应该可以使用offsetof来确定成员的偏移量。

对于C,对齐是实现定义的,我们可以在C99标准草案6.7.2.1结构和并集说明符12段(在C11中,它将是第14段)中看到:

结构或联合对象的每个非位字段成员都以适合其类型的实现定义的方式对齐。

和段落13说:

在结构对象中,非位字段成员和位字段所在的单位resident的地址按声明的顺序增加。指向经过适当转换的结构对象指向其初始成员(或者如果该成员是位字段,然后到它所在的单元),反之亦然。可能有未命名的在结构对象中填充,但不在其开头。

对于C++,我们从标准草案9.2类成员13段中有以下类似的引用:

具有相同访问控制(第11条)的(非并集)类的非静态数据成员被分配,以便稍后的成员在类对象中具有更高的地址。具有不同访问控制的非静态数据成员的分配顺序未明确(第11条)。实施一致性要求可能会导致两个相邻成员不能立即相互分配;

和段落19说:

一个指向标准布局结构对象的指针,该对象使用interpret_cast进行了适当转换,指向其初始成员(或者,如果该成员是位字段,则为其所在的单元),反之亦然。[注:因此,在标准布局结构对象中可能存在未命名的填充,但不是在其开头,以实现适当的对准--尾注]

您使用的大小写并不是真正的边缘大小写,两个uint_8都足够小,可以放在内存中的同一个单词中,将每个uint-8放在uint_16中是没有用的。

一个更关键的情况是:

{
uint8_t a;
uint8_t b;
uint_32 c; // where is C, at &a+2 or &a+4 ?
/* other members */
} test;

无论如何,这将始终取决于目标体系结构和编译器。。。

K&R第二版(ANSI C)第6.4章(第138页)说:

然而,不要假设结构的大小是其成员大小的总和。由于不同对象的对齐要求,结构中可能存在未命名的"孔"。

因此,ANSI C不保证b处于偏移量1。

编译器甚至可能将b放在偏移量sizeof(int)处,这样它就可以根据机器字的大小对齐,这更容易处理。

有些编译器支持pack pragma,这样您就可以强制struct中没有这样的"漏洞",但这是不可移植的。

其他答案已经提到了C标准所保证的内容。

但是,为了确保b在偏移量1,编译器可能会提供"打包"结构的选项,即显式添加填充。

对于gcc,这可以通过#pragmapack()来实现。

#pragma pack(1)
struct
{
uint8_t a; /* Guaranteed to be at offset 0. */
uint8_t b; /* Guaranteed to be at offset 1. */
/* other members are guaranteed to start at offset 2. */
} test_packed;
#pragma pack()
struct
{
uint8_t a; /* Guaranteed to by at offset 0. */
uint8_t b; /* NOT guaranteed to be at offset 1. */
/* other members are NOT guaranteed to start at offset 2. */
} test_unpacked;

一个可移植(和保存)的解决方案是简单地使用一个数组:

struct
{
uint8_t ab[2]; /* ab[0] is guaranteed to be at offset 0. */
/* ab[1] is guaranteed to be at offset 1. */
/* other members are NOT guaranteed to start at offset 2. */
} test_packed;