我正在尝试检查我的一些代码是否存在严格的别名违规行为,但是它
看起来我在尝试理解严格的别名时错过了一些东西
规则。
想象以下代码:
#include <stdio.h>
int main( void )
{
unsigned long l;
l = 0;
*( ( unsigned short * )&l ) = 1;
printf( "%lu\n", l );
return 0;
}
经典和基本示例。使用GCC 4.9(
-Wall -fstrict-aliasing -Wstrict-aliasing -O3
),它实际上报告了错误: dereferencing type-punned pointer will break strict-aliasing rules
但是以下代码可以很好地编译:
#include <stdio.h>
int main( void )
{
unsigned long l;
unsigned short * sp;
l = 0;
sp = ( unsigned short * )&l;
*( sp ) = 1;
printf( "%lu\n", l );
return 0;
}
以我的理解,第二个示例也违反了结构别名规则。
那为什么要编译呢?是GCC中的准确性问题,还是我错过了
有严格别名的东西?
我还找到了以下主题: Why are no strict-aliasing warnings generated for this code?
使用
-Wstrict-aliasing=2
或-Wstrict-aliasing=3
编译没有区别。但是
-Wstrict-aliasing=1
确实在第二个示例中报告了错误。GCC文档说,级别1的准确性最低,可以产生很多
误报,而第3级是最准确的...
那么这是怎么回事?我个人理解的问题或GCC的问题?
奖励问题
我的项目通常比GCC更喜欢Clang / LLVM,但似乎Clang
不会发出有关严格别名的任何警告。
有谁知道为什么?
是因为它无法检测到违规,还是因为它没有遵循
生成代码时的规则?
最佳答案
您的理解是正确的。别名分析通常很复杂,在这种情况下,显然仅在强制转换和取消引用之间使用临时指针就足以将其丢弃。令人惊讶的是,GCC 4.8.2在此代码上做得更好,在-Wstrict-aliasing=2
以及1级上都发出警告,因此这是一个回归。
至于clang,它目前根本没有警告别名冲突的功能。它确实在优化中充分利用了规则。要实际操作,请直接从C标准(N1570§6.5.2.39)来看这个示例。
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;
}
如果p1和p2指向相同的结构,那么Clang(和GCC)将在否定之前返回
p1->m
的值,因为它们可能会假定p2没有别名p1,因此先前的否定永远不会影响结果。 Here's the full example并输出带有和不带有-fstrict-aliasing
的输出。有关更多示例,请参见here和经常被引用的What Every C Programmer Should Know About Undefined Behavior。严格的别名优化是介绍性文章的最后主题。至于何时会实现警告the devs are quiet,但提到了in clang's test suite,它在标题(强调我的名字)下列出了
-Wstrict-aliasing=X
。因此,它似乎有可能在某个时候发生。