【发布时间】:2011-02-23 18:47:49
【问题描述】:
你有什么恐怖故事要讲吗? 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 away:f1() 只是存储了预先计算的答案:
movl $-2147483648, %eax
f2() 将 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
( = search page on Google 然后查看缓存页面)
What is the strict aliasing rule?
C99 strict aliasing rules in C++ (GCC)
在第一个链接中,七个月前的 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。
【问题讨论】:
-
你编译的优化级别是什么?优化级别越高,编译器就越有可能依赖严格的别名规则。 (顺便说一句,委员会会议记录中的引用可能适用于 ISO 标准的许多部分:-P)
-
小点:你可能应该使用
int64_t来确保联合中的整数元素与double的大小相同。 -
请注意,工会可能比其每个成员有更强的对齐要求。
-
John Regehr 给出了两个有趣、简短的 GCC 和 Clang 不一致示例。
标签: c gcc type-conversion unions strict-aliasing