除了第一个与打包结构不同的全打包字段的 C 结构吗?

Is a C struct of all-packed fields except first one different from packed struct?

在其 Common Type Attributes 中,GCC 提供 packed:

This attribute, attached to a struct [...] type definition, specifies that each of its members (other than zero-width bit-fields) is placed to minimize the memory required. This is equivalent to specifying the packed attribute on each of the members.

此外,根据 C18 标准 (§ 6.7.2.1, 15-17):

Within a structure object, the non-bit-field members [...] have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member [...], and vice versa. There may be unnamed padding within a structure object, but not at its beginning. [...] There may be unnamed padding at the end of a structure [...].

因此,鉴于结构的开头没有填充,在其第一个成员上使用 packed 似乎是多余的。反过来(这就是我的问题所在),在其所有成员 上使用 packed 除了第一个 似乎不必要地复杂并且等同于在上使用 packed结构本身(我们假设所有成员都是整数类型)。

但是我多次遇到类似于以下的代码:

struct S {
    unsigned a;
    unsigned b __attribute__ ((__packed__));
    unsigned c __attribute__ ((__packed__));
    unsigned d __attribute__ ((__packed__));
};

我不明白为什么作者更喜欢这样的斗争而不是

struct S {
    unsigned a;
    unsigned b;
    unsigned c;
    unsigned d;
} __attribute__ ((__packed__));

第一个成员的 packed 会改变结构本身的对齐问题吗?它是否来自我没有考虑过的填充和对齐以外的其他东西(例如旧版本GCC中的怪癖)?另一方面,如果它们是等效的,鉴于文档是恕我直言,在这里帮助不大,我怎么能确定呢?

虽然这不是证明它们等价的有效证据,但我尝试为各种架构(x86、x86_64、Aarch64)编译两个 struct,它们似乎总是共享相同的布局。

如果第一个成员没有打包,它仍然有它原来的对齐要求,可能大于一个字节,因此结构至少有那个对齐要求,这也可能需要在末尾填充结构。

对于:

struct S {
    unsigned a;
    unsigned b __attribute__ ((__packed__));
    unsigned c __attribute__ ((__packed__));
    unsigned d __attribute__ ((__packed__));
};

GCC 10.2 for x86-64 表示 _Alignof(struct S) 是 4。然而,如果 a 或整个结构被标记为 __attribute__ ((__packed__));,它表示对齐是 1.

如果我们将最后一个成员更改为char:

struct S {
    unsigned a;
    unsigned b __attribute__ ((__packed__));
    unsigned c __attribute__ ((__packed__));
    char d __attribute__ ((__packed__));
};

然后GCC说它的大小是16字节,显示它添加了三个字节的填充。如果 a 或整个结构被标记为 __attribute__ ((__packed__)),则大小更改为 13。