0

在其通用类型属性中,GCC 提供packed

此属性附加到struct[...] 类型定义,指定放置其每个成员(除了零宽度位字段)以最小化所需的内存。这 相当于指定packed每个成员的属性

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

在结构对象中,非位域成员 [...] 的地址按声明顺序递增。指向结构对象的指针,经过适当转换,指向其初始成员 [...],反之亦然。结构对象内可能有未命名的填充,但不是在其开头。[...] 结构末尾可能有未命名的填充 [...]。

因此,鉴于结构的开头没有填充,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 中的怪癖)?另一方面,如果它们是等效的,我怎么能确定它,因为文档是恕我直言,在这里没有什么帮助?

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

4

1 回答 1

3

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

为了:

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

x86-64 的 GCC 10.2 表示_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。

于 2020-10-23T13:27:35.877 回答