严格别名和指向联合字段的指针
我有一个关于严格别名规则,工会和标准的问题。 假设我们有以下代码:
#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为真,编译器应该如何处理指向工会成员的指针? 它是标准中的问题, 允许这样的编译器行为吗? 如果不是 – 为什么?
- 一般而言,编译器使用正确代码的不同行为在任何情况下都是不允许的。 所以它似乎也是一个编译器错误(特别是如果将地址转换为union字段将在函数内部,SA不会破坏依赖性)。
C标准规定明确允许通过联合进行别名。
但是请检查以下代码:
void func(int *a, short *b) { *a = 1; printf("%f\n", *b); }
严格别名规则的目的是假定a
和b
不是别名。 但是你可以调用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见这里