严格别名和指向联合字段的指针

我有一个关于严格别名规则,工会和标准的问题。 假设我们有以下代码:

#include  union { int f1; short f2; } u = {0x1}; int * a = &u.f1; short * b = &u.f2; int main() { u.f1 = 1; *a += 1; u.f2 = 2; *b *= 2; printf( "%d %hd\n", *a, *b); return 0; } 

现在让我们来看看它是如何工作的:

 $ gcc-5.1.0-x86_64 tc -O3 -Wall && ./a.out 2 4 $ gcc-5.1.0-x86_64 tc -O3 -Wall -fno-strict-aliasing && ./a.out 4 4 

我们可以看到严格别名会破坏依赖关系。 此外,它似乎是一个正确的代码而不破坏严格别名规则。

  1. 结果是,在工会字段的情况下,铺设在该地址的对象是否与所有类型的工会成员兼容?
  2. 如果1为真,编译器应该如何处理指向工会成员的指针? 它是标准中的问题, 允许这样的编译器行为吗? 如果不是 – 为什么?
  3. 一般而言,编译器使用正确代码的不同行为在任何情况下都是不允许的。 所以它似乎也是一个编译器错误(特别是如果将地址转换为union字段将在函数内部,SA不会破坏依赖性)。

C标准规定明确允许通过联合进行别名。

但是请检查以下代码:

 void func(int *a, short *b) { *a = 1; printf("%f\n", *b); } 

严格别名规则的目的是假定ab不是别名。 但是你可以调用func(&u.f1, &u.f2);

为了解决这个难题,常识解决方案是说工会必须避免严格别名规则的“旁路许可”仅适用于通过名称访问工会成员的时间。

标准没有明确说明这一点。 可以认为“如果成员使用……”(6.5.2.3)实际上是指定“旁路”仅在按名称访问成员时发生,但它不是100%清除。

然而,很难提出任何替代和自我一致的解释。 一种可能的替代解释是,写入func(&u.f1, &u.f2)会导致UB,因为重叠的对象被传递给一个“知道”它不会接收重叠对象的函数 – 有点像restrict违规。

如果我们将第一个解释应用于您的示例,我们会说printf中的*a会导致UB,因为存储在该位置的当前对象是short ,并且6.5.2.3没有启动,因为我们没有使用联合会员名称。

我猜根据你发布的结果,gcc正在使用相同的解释。

这已在此处讨论过,但我现在找不到该主题。

C99技术勘误3通过在6.5.2.3节中说明,基于联合方法澄清了类型 – 打孔:

如果用于访问union对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的适当部分将重新解释为新类型中的对象表示forms在6.2.6中描述(一个过程有时称为“类型双关语”)。

从1042到1044见这里