你有什么恐怖故事要讲吗?GCC 手册最近添加了关于 -fstrict-aliasing 和通过联合强制转换指针的警告:
[...]获取地址,转换结果指针并取消引用结果具有未定义的行为[强调添加],即使转换使用联合类型,例如:
union a_union {
int i;
double d;
};
int f() {
double d = 3.0;
return ((union a_union *)&d)->i;
}
有没有人有一个例子来说明这种未定义的行为?
请注意,这个问题与 C99 标准所说的或没有说的无关。它是关于今天gcc和其他现有编译器的实际功能。
我只是猜测,但一个潜在的问题可能在于设置d
为 3.0。因为d
它是一个永远不会直接读取的临时变量,也永远不会通过“有点兼容”的指针读取,所以编译器可能不会费心去设置它。然后 f() 将从堆栈中返回一些垃圾。
我简单、天真的尝试失败了。例如:
#include <stdio.h>
union a_union {
int i;
double d;
};
int f1(void) {
union a_union t;
t.d = 3333333.0;
return t.i; // gcc manual: 'type-punning is allowed, provided...' (C90 6.3.2.3)
}
int f2(void) {
double d = 3333333.0;
return ((union a_union *)&d)->i; // gcc manual: 'undefined behavior'
}
int main(void) {
printf("%d\n", f1());
printf("%d\n", f2());
return 0;
}
工作正常,给予CYGWIN:
-2147483648
-2147483648
查看汇编程序,我们看到gcc完全优化t
了: f1()
简单地存储预先计算的答案:
movl $-2147483648, %eax
whilef2()
将 3333333.0 推入浮点堆栈,然后提取返回值:
flds LC0 # LC0: 1246458708 (= 3333333.0) (--> 80 bits)
fstpl -8(%ebp) # save in d (64 bits)
movl -8(%ebp), %eax # return value (32 bits)
并且这些函数也是内联的(这似乎是一些微妙的严格混叠错误的原因),但这与这里无关。(而且这个汇编器没有那么重要,但它增加了确凿的细节。)
另请注意,获取地址显然是错误的(或正确的,如果您试图说明未定义的行为)。例如,正如我们知道这是错误的:
extern void foo(int *, double *);
union a_union t;
t.d = 3.0;
foo(&t.i, &t.d); // undefined behavior
我们同样知道这是错误的:
extern void foo(int *, double *);
double d = 3.0;
foo(&((union a_union *)&d)->i, &d); // undefined behavior
有关这方面的背景讨论,请参见例如:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1422.pdf
http://gcc.gnu.org/ml/gcc/2010-01/msg00013.html
http:// davmac.wordpress.com/2010/02/26/c99-revisited/
http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html(=
在 Google
上搜索页面然后查看缓存页面)
什么是严格的别名规则?
C++ (GCC) 中的 C99 严格别名规则
在第一个链接中,七个月前 ISO 会议的会议记录草稿,一位参与者在第 4.16 节中指出:
有没有人认为规则足够清晰?没有人能够真正解释它们。
其他说明:我的测试是使用 gcc 4.3.4,使用 -O2;选项 -O2 和 -O3 暗示 -fstrict-aliasing。GCC 手册中的示例假定 sizeof(double) >= sizeof(int); 他们是否不平等并不重要。
此外,正如 Mike Acton 在 cellperformace 链接中所指出的,这里的示例生成了-Wstrict-aliasing=2
,但不是。 =3
warning: dereferencing type-punned pointer might break strict-aliasing rules