#pragma pack(push, n)/#pragma pack(pop) 和 GCC 中的 __attribute__((__packed__, aligned(n))) 有什么区别?它们在 IT 技术中是什么意思?

17

就GCC而言(即使用GCC编译),以下两种方式的工作方式有何不同?

struct foo1 {
    char a;
    int b;
} __attribute__((__packed__, aligned(n) ));

并且:

#pragma pack(push, n)
struct foo2 {
    char a;
    int b;
};
#pragma pack(pop)

他们似乎表现不同

foo1 f1;
foo2 f2;

int& i1 = f1.b; // ok
int& i2 = f2.b; // cannot bind packed field 'f2.foo2::b' to 'int&'

为什么一个会出错而另一个不会?它们的内存布局是否相同?

1个回答

14
您没有说明正在使用哪个版本的GCC,但您可以在在线手册中找到相应的手册。在这些方面,它们都是相当兼容的,然而,一旦定义了属性和编译指示符的行为,通常会为了兼容性而保持不变。 我将从GCC 4.9.3手册中摘取具体引文,这是当前可用版本中最新的可用版本之一。 特别是涉及类型属性结构填充编译指示符的章节。
GCC手册对#pragma pack和其它类似指令的说法是:
“编译指示符可以更改随后定义的结构(非零宽度位域以外)的成员的最大对齐方式。”
(重点加粗)。 对于__attribute__((packed)),手册则表示:
“此属性附加到结构或联合类型定义上,指定结构体或联合体的每个成员(非零宽度位域以外)的布局以最小化所需内存。”
手册对__attribute__ ((aligned(n)))的说法是:
“此属性指定指定类型的变量最小对齐方式,以字节为单位。”
(重点加粗)。
因此,通常情况下,#pragma pack(n)(带或不带push)与将__attribute__((packed, aligned(n))附加到结构类型上并不相同。前者指定受影响结构的成员在n字节或更高字节边界上对齐。而后者指定受影响结构的成员被紧凑排列,并且整个结构实例的所需对齐要求必须不少于n。它们不仅不相同,甚至也不十分相似。
您会发现,将#pragma pack(1)应用于结构定义会对实例的布局产生与将__attribute__((packed))附加到该结构定义中相同的效果。然而,即使它们达到了相同的目标,它们也不是完全相同的事物。它们的行为和效果超出了C++规范范围,GCC完全有权在其他方面以不同的方式处理它们。
但是,如果您想使用属性来影响结构成员的对齐方式,则需要按成员逐个应用至少一些属性。例如...
struct foo1 {
    char a;
    int b __attribute__((aligned(n)));
} __attribute__((packed));

可能会产生与...相同的效果。

#pragma pack(push, n)
struct foo2 {
    char a;
    int b;
};
#pragma pack(pop)

...,根据n的不同而有所不同。


你应该发现 #pragma pack(1) 对结构体定义的影响与将 attribute((packed)) 附加到该定义上具有相同的效果。然而,在 OP 发布的示例中,一个编译,另一个不编译。 - m.s.
@m.s. 对我来说,它们都可以在g++ 4.4.7上编译通过。不过,他的原始示例也是如此。尽管如此,我还是修改了我的答案,在那个领域进行了一些缩小和加强。 - John Bollinger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接