我正在尝试检查我的一些代码是否存在严格的别名冲突,但它 看起来我在试图理解严格的混叠时错过了一些东西 规则。
想象一下以下代码:
#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;
}
根据我的理解,第二个例子也违反了结构别名规则 那为什么要编译呢?这是海湾合作委员会的准确性问题,还是我错过了 有严格别名的东西?
我还找到了以下主题: Why are no strict-aliasing warnings generated for this code?
使用-Wstrict-aliasing=2
或-Wstrict-aliasing=3
进行编译没有任何区别
但-Wstrict-aliasing=1
会在第二个示例中报告错误。
GCC文档说1级是最不准确的,并且可以产生很多 误报,而3级是最准确的...
那么这里发生了什么?我自己理解的问题还是GCC的问题?
加分问题
对于我的项目,我通常更喜欢Clang / LLVM而非GCC,但似乎是Clang
不会发出任何关于严格别名的警告
有谁知道为什么?
是因为它无法检测到违规行为,或者因为它没有遵循
生成代码时的规则?
答案 0 :(得分:8)
您的理解是正确的。别名分析通常很复杂,在这种情况下,显然仅使用强制转换和取消引用之间的临时指针就足以将其抛弃。令人惊讶的是,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
(强调我的)
这些标志目前未实现;测试我们输出它们 反正。
所以似乎有可能在某个时候发生。
答案 1 :(得分:7)
没有任何内在错误
sp = ( unsigned short * )&l;
对于所有编译器都知道,您可能只是简单地将指针强制转换为正确的类型。
也没有任何内在错误
*( sp ) = 1;
这两者的组合是错误的。
编译器只是不能把两者放在一起告诉你组合是有问题的(在一般情况下是不可能的,在这种特殊情况下不是那么容易)。
但是
*( ( unsigned short * )&l ) = 1;
更容易检测,因此编译器会尽力这样做。
答案 2 :(得分:2)
我希望在O3编译器决定因为它知道不同类型的变量不能别名,所以永远不会使用sp
。因此,它会从程序中删除sp
,并且不会产生错误。
要么是因为l
是已知值,要将其转换为常量以供printf使用。它仍然具有存储空间,因为它的地址已被占用,但该存储空间永远不会被写入。
答案 3 :(得分:2)
我对情况的解释:
*( ( unsigned short * )&l ) = 1;
更容易捕获,因为违规行为在一行中。所以它总是被抓住。
通过优化,第二个变体也被捕获。优化后,它就像第一个一样简单。
如果没有优化,-Wstrict-aliasing=1
(比默认值更懒)会抓住它。这是因为它将转换本身视为违规,即使没有取消引用(删除取消引用并查看)。这种简单的逻辑很快,但会导致误报。