在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)以及C ++ 98(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
指示的问题 不 是首选。还欢迎其他C编译器。
已添加6月2日 :Michael Burr's answer中的第一个链接 确实 符合条件一个恐怖的故事,可能有点过时(从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被复制到流中)。 / p>
编译代码,在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这是否合适,但到目前为止没有人不同意。
答案 0 :(得分:28)
没有我自己的恐怖故事,但这里有Linus Torvalds的一些引用(对不起,如果这些已经在问题中的链接参考之一):
http://lkml.org/lkml/2003/2/26/158:
2003年2月26日星期三09:22:15 -0800 主题Re:没有-fno-strict-aliasing的编译无效 来自Jean Tourrilhes&lt;&gt;
2003年2月26日星期三04:38:10 PM +0100,Horst von Brand写道:
Jean Tourrilhes&lt;&gt;表示:
对我来说,它看起来像编译器错误... 有些用户抱怨说下面的代码是 在没有-fno-strict-aliasing的情况下编译,写入的顺序和 memcpy被反转(这意味着伪造的len被复制到了 流)。 代码(来自linux / include / net / iw_handler.h):
恕我直言,编译器应该有足够的上下文来知道 重新排序是危险的。任何建议使这个简单的代码更多 欢迎使用防弹。static inline 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; }
编译器可以自由地假设char * stream和struct iw_event * iwe指向 由于严格的别名,分离内存区域。
这是真的,这不是我抱怨的问题。
(事后注意:这段代码很好,但Linux的memcpy
was a macro that cast to long *
实现要以更大的块进行复制。使用正确定义的memcpy
,gcc -fstrict-aliasing
不是不允许破解这段代码。但是如果编译器不知道如何将字节复制循环转换为高效的asm,那么就需要内联asm来定义内核memcpy
,gcc7之前的gcc就是这种情况。 )
Linus Torvald对上述内容的评论:
Jean Tourrilhes写道: &GT;
对我来说,它看起来像编译器错误......
为什么你认为内核使用“-fno-strict-aliasing”?
gcc人们更有兴趣尝试找出可能的内容 允许使用c99规范,而不是实际上工作。该 特别是别名代码甚至不值得启用,它只是没有 当有些东西可以别名时,可以很好地告诉gcc。
有些用户抱怨说下面的代码是 在没有-fno-strict-aliasing的情况下编译,写入的顺序和 memcpy被反转(这意味着伪造的len被复制到了 流)。
“问题”是我们内联memcpy(),此时gcc不会 关心它可以别名的事实,所以他们只是重新订购 一切都声称它是自己的错。即使没有理智 我们甚至可以告诉gcc它的方式。
几年前我试图找到一个理智的方式,而gcc的开发人员确实如此 不关心这个领域的现实世界。如果那样的话,我会感到惊讶 从我已经看过的回复来看,已经改变了。我不打算去打它。
莱纳斯
http://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg01647.html:
基于类型的别名是愚蠢的。这太令人难以置信的愚蠢,甚至都不好笑。它坏了。而gcc采取了破碎的观念,并通过使其成为“通过法律的信件”这一事实而更加明智。
...
我知道事实 gcc会重新排序明确(静态)相同地址的写访问。 Gcc突然想到了
可以重新命令unsigned long a; a = 5; *(unsigned short *)&a = 4;
将其设置为4(因为很明显它们不是别名 - 通过阅读标准),然后因为现在'a = 5'的赋值是后来的,4的赋值可能是完全省略了!如果有人抱怨编译器是疯了,编译人员会说“nyaah,nyaah,标准人们说我们可以做到这一点”,绝对没有反省询问是否有任何SENSE。
答案 1 :(得分:7)
SWIG会生成依赖于严格别名关闭的代码,这会导致all sorts of problems。
SWIGEXPORT jlong JNICALL Java_com_mylibJNI_make_1mystruct_1_1SWIG_12(
JNIEnv *jenv, jclass jcls, jint jarg1, jint jarg2) {
jlong jresult = 0 ;
int arg1 ;
int arg2 ;
my_struct_t *result = 0 ;
(void)jenv;
(void)jcls;
arg1 = (int)jarg1;
arg2 = (int)jarg2;
result = (my_struct_t *)make_my_struct(arg1,arg2);
*(my_struct_t **)&jresult = result; /* <<<< horror*/
return jresult;
}
答案 2 :(得分:4)
gcc,别名和二维可变长度数组:以下示例代码复制了一个2x2矩阵:
#include <stdio.h>
static void copy(int n, int a[][n], int b[][n]) {
int i, j;
for (i = 0; i < 2; i++) // 'n' not used in this example
for (j = 0; j < 2; j++) // 'n' hard-coded to 2 for simplicity
b[i][j] = a[i][j];
}
int main(int argc, char *argv[]) {
int a[2][2] = {{1, 2},{3, 4}};
int b[2][2];
copy(2, a, b);
printf("%d %d %d %d\n", b[0][0], b[0][1], b[1][0], b[1][1]);
return 0;
}
在CentOS上使用gcc 4.1.2 ,我得到:
$ gcc -O1 test.c && a.out
1 2 3 4
$ gcc -O2 test.c && a.out
10235717 -1075970308 -1075970456 11452404 (random)
我不知道这是否是众所周知的,我不知道这是一个bug还是一个功能。 我无法在Cygwin 上使用gcc 4.3.4 复制问题,因此可能已修复。一些解决方法:
__attribute__((noinline))
复制()。-fno-strict-aliasing
。b[][n]
更改为b[][2]
。-O2
或-O3
。补充说明:
copy()
。 (而且,顺便说一下,我看到gcc没有展开双循环,我感到有些惊讶。)-Wstrict-aliasing=
,在这里做了什么。更新:以上并没有真正回答OP的问题,因为他(即我)正在询问严格别名“合法地”破坏您的代码的情况,而以上似乎只是一个花园式的编译器错误。
我向GCC Bugzilla报告,但他们对旧的4.1.2不感兴趣,尽管(我相信)它是10亿美元RHEL5的关键。它不会出现在4.2.4上。
我有一个稍微简单的类似bug的例子,只有一个矩阵。代码:
static void zero(int n, int a[][n]) {
int i, j;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
a[i][j] = 0;
}
int main(void) {
int a[2][2] = {{1, 2},{3, 4}};
zero(2, a);
printf("%d\n", a[1][1]);
return 0;
}
产生结果:
gcc -O1 test.c && a.out
0
gcc -O1 -fstrict-aliasing test.c && a.out
4
似乎组合-fstrict-aliasing
与-finline
会导致错误。
答案 3 :(得分:2)
以下代码在gcc 4.4.4下返回10。 union方法或gcc 4.4.4是否有问题?
int main()
{
int v = 10;
union vv {
int v;
short q;
} *s = (union vv *)&v;
s->v = 1;
return v;
}
答案 4 :(得分:2)
这是我的:
http://forum.openscad.org/CGAL-3-6-1-causing-errors-but-CGAL-3-6-0-OK-tt2050.html
它导致CAD程序中的某些形状被错误地绘制。谢天谢地,项目负责人致力于创建回归测试套件。
该错误仅在某些平台上表现出来,旧版本的GCC和某些库的旧版本。然后只有-O2打开。 -fno-strict-aliasing解决了它。
答案 5 :(得分:2)
C的公共初始序列规则曾被解释为制作它 可以编写一个可以在a的前导部分工作的函数 各种各样的结构类型,只要它们从匹配元素开始 类型。在C99下,规则已更改,以便仅在结构中应用 涉及的类型是同一联盟的成员,其完整的声明在使用时可见。
gcc的作者坚持认为所讨论的语言只适用于 尽管有事实,但是通过联合类型执行访问 的是:
如果必须通过联合类型执行访问,则没有理由指定 complete 声明必须可见。
虽然CIS的规则是以工会的形式来描述的,但它是主要的 有用的在于它对结构形式的暗示 布局和访问。如果S1和S2是共享CIS的结构, 没有办法接受指向S1的指针 来自外部来源的S2可以符合C89的CIS规则 不允许相同的行为对指针有用 实际上不在union对象中的结构;指定CIS 因此,对结构的支持将是多余的 已经为工会指定。