Mac*_*ade 20 c gcc c99 strict-aliasing
我正在尝试检查我的一些代码是否存在严格的别名违规,但在尝试理解严格的别名规则时,我似乎错过了一些东西.
想象一下以下代码:
#include <stdio.h>
int main( void )
{
unsigned long l;
l = 0;
*( ( unsigned short * )&l ) = 1;
printf( "%lu\n", l );
return 0;
}
Run Code Online (Sandbox Code Playgroud)
经典和基本的例子.使用GCC 4.9(-Wall -fstrict-aliasing -Wstrict-aliasing -O3),它实际报告错误:
dereferencing type-punned pointer will break strict-aliasing rules
Run Code Online (Sandbox Code Playgroud)
但以下编译很好:
#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;
}
Run Code Online (Sandbox Code Playgroud)
在我的理解中,第二个例子也违反了结构别名规则.
那为什么要编译呢?这是GCC中的准确性问题,还是我错过了严格混叠的问题?
我还发现了以下主题:为什么没有为此代码生成严格别名警告?
编译-Wstrict-aliasing=2或-Wstrict-aliasing=3没有区别.
但是-Wstrict-aliasing=1会在第二个例子中报告错误.
海湾合作委员会的文件说1级是最不准确的,可以产生很多误报,而3级是最准确的......
那么这里发生了什么?我自己理解的问题还是GCC的问题?
奖金问题
对于我的项目,我通常更喜欢Clang/LLVM而非GCC,但似乎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;
}
Run Code Online (Sandbox Code Playgroud)
如果p1和p2指向相同的结构,则Clang(和GCC)将返回p1->m否定之前的值,因为它们可能假设p2不会对p1进行别名,因此先前的否定决不会影响结果.这是完整的示例和输出,有和没有-fstrict-aliasing.有关更多示例,请参阅此处以及经常引用的每个C程序员应该知道的关于未定义行为的内容 ; 严格的别名优化是介绍性帖子的最后一个主题.
至于什么时候警告会被实施,开发人员很安静,但是在clang的测试套件中提到了它们,它-Wstrict-aliasing=X在标题下面列出(强调我的)
这些标志目前尚未实施; 无论如何测试我们输出它们.
因此,似乎有可能在某些时候发生.
没有什么本质上的错误
sp = ( unsigned short * )&l;
Run Code Online (Sandbox Code Playgroud)
对于所有编译器都知道,您可能只是再次将指针强制转换为正确的类型.
也没有什么本质上的错误
*( sp ) = 1;
Run Code Online (Sandbox Code Playgroud)
这是两者的结合,这是错误的.
编译器只是不能把两者放在一起告诉你组合是有问题的(在一般情况下这是不可能的,在这种特殊情况下并不那么容易).
但
*( ( unsigned short * )&l ) = 1;
Run Code Online (Sandbox Code Playgroud)
更容易检测,因此编译器尽力这样做.