在gcc-strict-aliasing-and-casting-through-a-union中,我问是否有人通过指针遇到工会打击问题 . 到目前为止,答案似乎是否定的 .
这个问题更广泛:你有关于gcc和严格别名的恐怖故事吗?
背景:引自AndreyT's answer in c99-strict-aliasing-rules-in-c-gcc:
“严格的别名规则植根于自[标准化]时代开始以来C和C中存在的标准部分 . 禁止通过另一种类型的左值访问一种类型的对象的条款存在于C89 / 90中( 6.3)以及C98(3.10 / 15)......只是并非所有编译器都希望(或敢于)强制执行或依赖它 .
好吧, gcc 现在敢于这样做,其 -fstrict-aliasing
开关 . 这引起了一些问题 . 例如,请参阅关于Mysql错误的优秀文章http://davmac.wordpress.com/2009/10/,以及http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html中同样出色的讨论 .
其他一些不太相关的链接:
重复一遍,你有自己的恐怖故事吗? -Wstrict-aliasing
表示的问题 not 当然是首选 . 其他C编译器也很受欢迎 .
Added June 2nd :迈克尔伯尔的答案中的第一个链接,其确定 indeed 有资格作为一个恐怖故事,可能有点过时(从2003年开始) . 我做了一个快速测试,但问题显然已经消失了 .
资源:
#include <string.h>
struct iw_event { /* dummy! */
int len;
};
char *iwe_stream_add_event(
char *stream, /* Stream of events */
char *ends, /* End of stream */
struct iw_event *iwe, /* Payload */
int event_len) /* Real size of payload */
{
/* Check if it's possible */
if ((stream + event_len) < ends) {
iwe->len = event_len;
memcpy(stream, (char *) iwe, event_len);
stream += event_len;
}
return stream;
}
具体投诉是:
有些用户抱怨说,当编译[above]代码而没有-fno-strict-aliasing时,write和memcpy的顺序会被反转(这意味着虚假len被mem复制到流中) .
编译代码,在CYGWIN wih -O3上使用 gcc 4.3.4(如果我错了请纠正我 - 我的汇编程序有点生锈!):
_iwe_stream_add_event:
pushl %ebp
movl %esp, %ebp
pushl %ebx
subl $20, %esp
movl 8(%ebp), %eax # stream --> %eax
movl 20(%ebp), %edx # event_len --> %edx
leal (%eax,%edx), %ebx # sum --> %ebx
cmpl 12(%ebp), %ebx # compare sum with ends
jae L2
movl 16(%ebp), %ecx # iwe --> %ecx
movl %edx, (%ecx) # event_len --> iwe->len (!!)
movl %edx, 8(%esp) # event_len --> stack
movl %ecx, 4(%esp) # iwe --> stack
movl %eax, (%esp) # stream --> stack
call _memcpy
movl %ebx, %eax # sum --> retval
L2:
addl $20, %esp
popl %ebx
leave
ret
而对于Michael的回答中的第二个链接,
*(unsigned short *)&a = 4;
gcc 通常(总是?)发出警告 . 但我相信对此有效的解决方案(对于 gcc )是使用:
#define CAST(type, x) (((union {typeof(x) src; type dst;}*)&(x))->dst)
// ...
CAST(unsigned short, a) = 4;
我已经问过gcc-strict-aliasing-and-casting-through-a-union这是否合适,但到目前为止没有人不同意 .
6 回答
没有我自己的恐怖故事,但这里有来自Linus Torvalds的一些引用(对不起,如果这些已经在问题中的一个链接引用中):
http://lkml.org/lkml/2003/2/26/158:
(事后注意:这段代码很好,但是Linux的
memcpy
was a macro that cast to long *实现了更大的块 . 使用正确定义的memcpy
,gcc -fstrict-aliasing
不允许破坏这段代码 . 但这意味着你需要内联asm来定义内核memcpy
如果您的编译器不知道如何将字节复制循环转换为高效的asm,gcc7之前的gcc就是这种情况)http://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg01647.html:
a = 5;
(无符号短)&a = 4;
可以重新命令将它设置为4(因为很明显它们没有别名 - 通过阅读标准),然后因为现在'a = 5'的分配是后来的,4的分配可以完全省略!如果有人抱怨编译器是疯了,编译人员会说“nyaah,nyaah,标准人们说我们可以做到这一点”,绝对没有反省询问是否有任何SENSE .
SWIG生成的代码依赖于严格的别名关闭,这可能会导致all sorts of problems .
gcc, aliasing, and 2-D variable-length arrays: 以下示例代码复制2x2矩阵:
使用CentOS上的gcc 4.1.2 ,我得到:
我不知道这是一个bug还是一个功能 . 我不能在Cygwin上用gcc 4.3.4 复制问题,所以它可能已被修复 . 一些解决方法:
使用
__attribute__((noinline))
进行复制() .使用gcc开关
-fno-strict-aliasing
.将copy()的第三个参数从
b[][n]
更改为b[][2]
.不要使用
-O2
或-O3
.附加说明:
这是一年又一天之后的回答,我自己的问题(我有点惊讶,只有两个答案) .
我的实际代码卡尔曼滤波器丢失了几个小时 . 看似微不足道的变化会产生巨大的影响,也许是因为改变gcc 's automatic inlining (this is a guess; I'仍然不确定) . 但它可能不符合恐怖故事的条件 .
是的,我知道你不会这样写
copy()
. (而且,作为一个旁边,我有点惊讶地看到gcc没有展开双循环 . )没有gcc警告开关,包括
-Wstrict-aliasing=
,在这里做了什么 .1-D可变长度数组似乎没问题 .
Update :以上并没有真正回答OP 's question, since he (i.e. I) was asking about cases where strict aliasing '合法地'破坏了你的代码,而上述似乎只是一个花园式的编译器bug .
我把它报告给了GCC Bugzilla,但它们并没有发生在4.2.4以上 .
我有一个稍微简单的类似bug的例子,只有一个矩阵 . 代码:
产生结果:
它似乎是组合
-fstrict-aliasing
与-finline
导致错误 .以下代码在gcc 4.4.4下返回10 . 联合方法或gcc 4.4.4有什么问题吗?
这是我的:
http://forum.openscad.org/CGAL-3-6-1-causing-errors-but-CGAL-3-6-0-OK-tt2050.html
它导致CAD程序中的某些形状被错误地绘制 . 谢天谢地,项目负责人致力于创建回归测试套件 .
该错误仅在某些平台上表现出来,旧版本的GCC和某些库的旧版本 . 然后只有-O2打开 . -fno-strict-aliasing解决了它 .
C的公共初始序列规则曾经被解释为可以编写一个可以在各种结构类型的前导部分工作的函数,前提是它们以匹配类型的元素开始 . 在C99下,规则被更改,以便仅在涉及的结构类型是同一联盟的成员时才应用,其完整声明在使用点可见 .
gcc的作者坚持认为,只有通过联合类型执行访问时,所讨论的语言才适用,尽管有以下事实:
如果必须通过union类型执行访问,则没有理由指定必须显示完整声明 .
虽然CIS的规则是以工会的形式来描述的,但它的主要用处在于它对结构的布局和访问方式的暗示 . 如果S1和S2是共享CIS的结构,那么就会存在从外部源接受指向S1和S2的指针的函数不能遵守C89的CIS规则,而不允许相同的行为对指向实际不在union对象内的结构的指针有用 . 因此,指定CIS对结构的支持将是多余的,因为它已经为工会指定 .