单成员结构体的结构填充

4
我希望创建一个结构体,其大小在编译时是任意的且已知(用于宏)。例如:
/* assume sizeof(SomeStruct) could be an odd number,
 * if it is using GCC's 'packed' attribute for eg */
struct {
    unsigned char data[sizeof(SomeStruct)];
} a, *tmp;

tmp = (void *)some_data
a = *tmp;

然而,我担心struct填充会增加struct的大小,使其比成员大。但是有人向我保证,单个成员struct的大小总是等于其成员的大小。

因此,我的问题是:

我能否依赖于单个成员结构体始终与其成员具有相同的大小?这是C规范的一部分吗?还是只是大多数编译器的行为?


我见过编译器在只有一个成员的对象中填充东西,所以我猜不会。没有硬引用。 - Alexander Oh
在http://stackoverflow.com/questions/29192256的第四个评论中,@Jens Gustedt说不会有填充,但没有引用来源。我在Linux/GCC中进行了检查,发现奇数没有填充,但这并不意味着它在所有其他平台/架构上都能起作用。 - ideasman42
@Hans Passant,关于定义临时存储结构体,请参见SWAP宏的示例 - http://stackoverflow.com/questions/29192256/portable-c-swap-macro-which-doesnt-require-a-type-argument - ideasman42
2
声明一个联合体,其中一个成员是SomeStruct,另一个成员是data[1]。 - Hans Passant
一个只有一个成员的联合也可以发挥作用。 - ideasman42
1个回答

6

C11 6.7.2.1第17款规定:

结构体或联合体可能在末尾有未命名的填充字段。

对于只有一个成员的结构体,没有特别的规定。这是由实现定义的。

尽管成员的大小已经足以满足所有需要,因为它本身就具有完整的类型,但编译器为什么会添加填充字段并不明显。


不过,你无需依赖编译器的行为,可以通过在宏中添加静态断言来自己强制执行sizeof *tmp == sizeof (SomeStruct)。如果你使用的是最新版本的编译器,并且你的宏允许声明语句,那么就可以使用_Static_assert;还有几种与C99兼容的hack方法可供使用,如下所示:

#define STATIC_EXPR_ASSERT(COND) (sizeof (char[(COND) ? 1 : -1]))

...可以用作逗号表达式的lhs。

您还应该注意,如果临时结构体的主体由char数组组成,则可能无法具有足够的对齐方式来正确表示传入的数据。从C11开始,您应该使用_Alignas指定数组的对齐方式:

struct {
    _Alignas (max_align_t) unsigned char data[sizeof(SomeStruct)];
} a, *tmp;

这将确保它可以安全地存储任何类型的数据,而不受对齐方式的影响。在C99中,您没有任何明确请求最大对齐方式的方法,但是您可以通过使临时结构成为其旁边的联合体成员来强制对齐方式与命名类型匹配(您可以尝试使用long double并希望它是最对齐的类型)。


2
假设有struct{char foo;}Foo;,那么sizeof(Foo)可能会大于sizeof(char)。这是因为编译器可以在任意边界上设置Foo的对齐方式,但为了强制执行sizeof(Foo[N])==N*sizeof(Foo),它必须在结构体末尾进行填充。 - user3528438
有没有任何编译器可以做到这一点? - ideasman42
@ideasman42 我有所怀疑,但有可能有人这样做是为了支持特殊情况(也许是作为 C11 之前的对齐注释?),或者在非常幼稚的编译器/解释器的情况下仅出于简单起见将所有结构体都最大对齐。 - Alex Celeste

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