2011-06-05 70 views
10

想象一下由32位,16位和8位成员值组成的结构。成员值的顺序是每个成员都在自然边界上。结构成员对齐 - 是否可以假设没有填充

struct Foo 
{ 
    uint32_t a; 
    uint16_t b; 
    uint8_t c; 
    uint8_t d; 
    uint32_t e; 
}; 

对于Visual C++,成员对齐和填充规则为documented。 sizeof(Foo)在VC++上,上面的结构可预测为“12”。

现在,我很确定规则是不应该对填充和对齐做出假设,但实际上,其他操作系统上的其他编译器是否也会做出类似的保证?

如果不是,GCC上是否有等价的“#pragma pack(1)”?

+0

在C中,'sizeof(Foo)'中的'Foo'没有声明。该表达式是一个错误(除非'Foo'被定义/在其他地方声明)。我建议你不要尝试编写多语言源文件:缺点比你可能找到的任何专业版都要大得多。 – pmg 2011-06-05 09:28:51

+0

@pmg:Pedantry。 :)'typedef struct Foo {...} Foo;' – Xeo 2011-06-05 09:33:31

+0

我相信'typedef'在C++中是多余的(因此是一个安全可移除的bug源)。我的观点仍然是:编写多语言源文件比它的价值更麻烦。 – pmg 2011-06-05 09:41:59

回答

6

在实际提供这些类型的系统上,它很可能工作。比方说,一个36位系统,这些类型首先不可用。

GCC提供了一个属性

__attribute__ ((packed))

随着类似的效果。

+0

它不是依赖于类型的,而是依赖于架构。例如,某些16位或32位处理器无法直接寻址8位地址,因此,如果没有填充,可能不得不生成额外的移位和屏蔽指令,以便从16位或32位存储器读取中提取8位值。这种情况比假设的36位系统(例如ARM7)更常见。 – Clifford 2011-06-05 10:16:34

+0

@Clifford:这不常见。如果编译器存在的话,编译器已经必须生成8位类型的代码(因为它不能在数组中对齐),所以它没有额外的限制。一般来说,类型的“对齐”决不能大于它的大小(并且必须划分它的大小),因此编译器在结构中使用类型时强加更大的对齐将是病态的。 – 2011-06-05 11:16:52

+0

我并不是说编译器在必要时无法或不会为8位访问生成代码,而是出于性能方面的考虑,它会避免使用结构。一个数组保证是连续的,一个结构不是。我会建议ARM7已经足够普及了,但是因为这是Linux的标签,所以在这种情况下并不多。 – Clifford 2011-06-06 19:46:31

7

总的来说,你是正确的,这不是一个安全的假设,虽然你会经常得到你期望在许多系统上的包装。当你使用gcc时,你可能想在你的类型上使用packed属性。

E.g.

struct __attribute__((packed)) Blah { /* ... */ }; 
+0

这是相当危险的,完全没有必要。看到我的答案。 – 2011-06-05 14:06:04

+0

@R:我完全同意。 – 2011-06-05 14:10:35

5

在实践中,如果uintXX_t类型存在任何系统上,你会得到不填充所需的对齐方式。不要扔在丑陋的海湾合作委员会,以保证它。

编辑:为了详细说明为什么它可能是有害的使用attribute packedaligned,它可能会导致整个结构作为更大结构的成员或在堆栈上使用时为错位。这肯定会损害性能,并且在非x86机器上会产生更大的代码。这也意味着它是无效采取指向结构的任何成员的指针,因为通过指针访问值的代码将不会意识到它可能会错位,因此可能会出错。

至于为什么没有必要,请记住attribute是特定于gcc和gcc-workalike编译器的。 C标准不会使对齐不明确或未指定。它是实现定义的这意味着执行是需要进一步指定和文件它的行为。 gcc的行为始终是,将每个结构成员与其自然对齐的下一个边界对齐(在结构之外使用时,它将具有相同的对齐方式,这必然是一个平均分割类型大小的数字) 。由于attribute是一个gcc特性,如果你使用它,你已经假设一个类似于gcc的编译器,但是假设你已经有了你想要的对齐方式。