联盟和C11中的严格别名

MrM*_*ter 6 strict-aliasing unions language-lawyer c11

假设我有这样的联盟

union buffer {
  struct { T* data; int count; int capacity; };
  struct { void* data; int count; int  capacity; } __type_erased;
};
Run Code Online (Sandbox Code Playgroud)

如果我在C11别名规则下将读/写混合到匿名结构成员和__type_erased成员,我会遇到麻烦吗?

更具体地说,我感兴趣的是如果独立访问组件(例如通过不同的指针)会发生的行为.为了显示:

grow_buffer(&buffer.__type_erased);
buffer.data[buffer.count] = ...
Run Code Online (Sandbox Code Playgroud)

我已经阅读了所有可以找到的相关问题,但我仍然没有100%明确这一点,因为有些人似乎认为这种行为是不明确的,而其他人则认为这是合法的.此外,我发现的信息是C++,C99,C11等规则的混合,很难消化.在这里,我对C11强制要求的行为以及流行编译器(Clang,GCC)展出的行为感兴趣

编辑:更多信息

我现在已经对多个编译器进行了一些实验,并决定分享我的发现以防有人遇到类似的问题.我的问题的背景是我试图在简单的C中编写一个用户友好的高性能通用动态数组实现.想法是使用宏执行数组操作并且重载操作(如增长数组)是使用别名类型擦除的模板结构执行.例如,我可以像这样的宏:

#define ALLOC_ONE(A)\
    (_array_ensure_size(&A.__type_erased, A.count+1), A.count++)
Run Code Online (Sandbox Code Playgroud)

必要时增长数组并返回新分配项的索引.规范(6.5.2.3)规定允许通过不同的联盟成员访问同一位置.我对此的解释是,虽然_array_ensure_size()不知道union类型,但编译器应该知道成员__type_erased可能会因副作用而发生变异.也就是说,我认为这应该有效.但是,这似乎是一个灰色区域(说实话,规范实际上并不清楚成员访问的构成).Apple的最新Clang(clang-800.0.33.1)没有任何问题.代码编译时没有警告,并按预期运行.但是,当使用GCC 5.3.0编译时,代码会因段错误而崩溃.事实上,我强烈怀疑GCC的行为是一个错误 - 我试图通过删除可变指针引用并采用明确的功能样式来明确联合成员变异,例如:

#define ALLOC_ONE(A) \
   (A.__type_erased = _array_ensure_size(A.__type_erased, A.count+1),\
    A.count++)
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,这再次与Clang合作,但再次崩溃了GCC.我的结论是,使用工会进行先进式操作是一个灰色区域,人们应该谨慎行事.

ale*_*ius 3

C11 标准规定如下:

\n\n
\n

6.5.2.3 结构和联合成员

\n\n

95) 如果用于读取联合对象内容的成员与最后用于在对象中存储值的成员不同,则该值的对象表示形式的适当部分将被重新解释为6.2.6 中描述的新类型中的对象表示(有时称为 \xe2\x80\x98\xe2\x80\x98type 双关语\xe2\x80\x99\xe2\x80\x99 的过程)。这可能是陷阱表示。

\n
\n\n

所以从C11中联合字段读/写的角度来看它是正确的。但严格别名是基于类型的分析,因此其简单的实现可以说这些读/写操作是独立的。据我了解,现代 gcc 可以检测具有联合字段的情况并避免此类错误。

\n\n

另外,您应该记住,在某些情况下,指向联合成员的指针是无效的:

\n\n
\n

以下不是有效的片段(因为联合类型在函数 f 中不可见):

\n\n
struct t1 { int m; };\nstruct t2 { int m; };\nint f(struct t1 *p1, struct t2 *p2)\n{\n  if (p1->m < 0)\n  p2->m = -p2->m;\n  return p1->m;\n}\nint g()\n{\n  union {\n    struct t1 s1;\n    struct t2 s2;\n  } u;\n  /* ... */\n  return f(&u.s1, &u.s2);\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n\n

在我看来,使用联合体在不同成员中进行读/写是危险的,最好避免它。

\n