以下代码段是C11 standard §6.5.2.3:
中的示例struct t1 { int m; };
struct t2 { int m; };
int f(struct t1 *p1, struct t2 *p2)
{
if (p1->m < 0)
p2->m = -p2->m;
return p1->m;
}
int g()
{
union {
struct t1 s1;
struct t2 s2;
} u;
/* ... */
return f(&u.s1, &u.s2);
}
根据C11,g()
内的最后一行无效。为什么这样?
答案 0 :(得分:6)
该示例来自ISO / IEC 9899:2011的§6.5.2.3结构和联合成员中的示例3。之前的段落之一是(强调添加):
¶6为了简化联合的使用,我们做了一个特别的保证:如果一个联合包含几个共享一个共同初始序列的结构(见下文),并且如果联合对象当前包含这些结构中的一个,那么它是允许检查其中任何一个的共同初始部分可以看到完整类型的联合声明。如果对应的成员具有一个或多个初始成员的序列的兼容类型(并且对于位字段,具有相同的宽度),则两个结构共享公共初始序列。
问题中引用的代码前面是注释:
以下不是有效的片段(因为联合类型在函数
f
中不可见)。
根据突出显示的陈述,现在这是有道理的。 g()
中的代码正在使用公共初始序列,但这仅适用于union
可见但在f()
中不可见的情况。
问题也是严格别名之一。这是一个复杂的话题。有关详细信息,请参阅What is the strict aliasing rule?。
无论价值如何,即使在严格的警告选项下,GCC 7.1.0也不会报告问题。 Clang也没有,即使使用-Weverything
选项:
clang -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
-Wstrict-prototypes -Weverything -pedantic …
答案 1 :(得分:3)
这是因为“有效类型”规则。如果您看到f
被隔离,则两个参数具有不同的类型,并且允许编译器执行某些优化。
此处,p1
被访问两次。如果p1
和p2
应该不同,则编译器不需要为p1
重新加载return
的值,因为它无法更改。
f
是有效代码,优化有效。
在g
中使用相同的对象调用它是无效的,因为没有看到两者都来自同一个union
,编译器可能不会采取措施来避免优化。
这是其中一种情况,其中证明调用有效的全部负担在于函数的用户,通常没有编译器可以在f
和g
发生时向您发出警告在不同的翻译单位。