我对严格别名规则,工会和标准有疑问。假设我们有以下代码:
#include <stdio.h>
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 t.c -O3 -Wall && ./a.out
2 4
$ gcc-5.1.0-x86_64 t.c -O3 -Wall -fno-strict-aliasing && ./a.out
4 4
我们可以看到严格别名会破坏依赖关系。此外,它似乎是一个正确的代码而不破坏严格别名规则。
答案 0 :(得分:3)
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
违规。
如果我们将第一个解释应用于您的示例,我们会说*a
中的printf
会导致UB,因为存储在该位置的当前对象是short
,并且6.5 .2.3没有启动,因为我们没有按名称使用工会成员。
我根据您发布的结果猜测gcc正在使用相同的解释。
这已在此处讨论过,但我现在找不到该主题。
答案 1 :(得分:2)
C99技术勘误3通过第6.5.2.3节中的说明阐明了基于联合方法的打字:
如果用于访问union对象内容的成员不是 与上次用于在对象中存储值的成员相同, 值的对象表示的适当部分是 如上所述,重新解释为新类型中的对象表示 在6.2.6(一个有时被称为“类型双关语”的过程)。
请参阅1042至1044的here