假设我有这样的工会
union buffer {
struct { T* data; int count; int capacity; };
struct { void* data; int count; int capacity; } __type_erased;
};
如果在C11别名规则下混合对匿名struct成员和__type_erased成员的读/写操作,我会遇到麻烦吗?
更具体地说,我对如果独立访问组件(例如通过不同的指针)时发生的行为感兴趣。为了显示:
grow_buffer(&buffer.__type_erased);
buffer.data[buffer.count] = ...
我已经阅读了所有可以找到的相关问题,但是我仍然不是100%清楚,因为有些人似乎暗示这种行为是不确定的,而另一些人则认为这是合法的。此外,我发现的信息是C++,C99,C11等规则的混合,很难消化。在这里,我对由C11强制执行并由流行的编译器(Clang,GCC)展示的行为非常感兴趣。
编辑:更多信息
我现在对多个编译器进行了一些实验,并决定分享我的发现,以防有人遇到类似问题。我的问题的背景是我试图在普通C语言中编写一个用户友好的高性能通用动态数组实现。该想法是使用宏执行数组操作,而重型操作(如增长数组)是使用别名类型擦除的模板结构执行。例如,我可以有这样的宏:
#define ALLOC_ONE(A)\
(_array_ensure_size(&A.__type_erased, A.count+1), A.count++)
必要时增加数组并返回新分配项的索引。规范(6.5.2.3)规定允许通过不同的工会成员访问同一位置。我对此的解释是,尽管_array_ensure_size()不了解联合类型,但编译器应注意,成员__type_erased可能因副作用而发生突变。也就是说,我认为这应该可行。但是,这似乎是一个灰色区域(老实说,规范实际上并不清楚什么构成成员访问权限)。苹果最新的Clang(clang-800.0.33.1)没问题。该代码在没有警告的情况下编译并按预期运行。但是,使用GCC 5.3.0编译时,代码会因段错误而崩溃。实际上,我非常怀疑GCC的行为是一个错误-我尝试通过删除可变指针ref并采用清晰的功能样式来使工会成员突变变得显式,例如:
#define ALLOC_ONE(A) \
(A.__type_erased = _array_ensure_size(A.__type_erased, A.count+1),\
A.count++)
如预期的那样,它再次适用于Clang,但再次使GCC崩溃。我的结论是,带有联合的高级类型操作是一个灰色区域,应谨慎使用。
最佳答案
C11标准说明以下内容:
因此,从联合字段在C11中读/写的角度来看是正确的。但是严格混叠是基于类型的分析,因此它的幼稚实现可以说这些读/写操作是独立的。据我了解,现代gcc可以检测带有并集字段的案例并避免此类错误。
阿洛索,您应该记住,在某些情况下,指向工会成员的指针是无效的:
在我看来,使用工会在不同成员中进行读/写是危险的,最好避免这样做。
关于language-lawyer - C11中的并集和严格别名,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38452208/