已关闭。此问题需要更多focused。它目前不接受回答。
**希望改进此问题?**更新问题,使其仅针对editing this post的一个问题。
三年前就关了。
Improve this question的
在gcc-strict-aliasing-and-casting-through-a-union中,我问过是否有人遇到过通过指针进行联合双关的问题。到目前为止,答案似乎是“不”。
这个问题的范围更广:你有关于gcc和严格别名的恐怖故事吗?
背景:引自AndreyT在c99-strict-aliasing-rules-in-c-gcc中的回答:
“严格的别名规则源于自[标准化]时代开始以来就存在于C和C中的部分标准。C89/90(6.3)和C98(3.10/15)中都有禁止通过一个类型的左值访问另一个类型的对象的子句。......只是,并非所有的编译者都希望(或敢于)执行或依赖它。”
好吧,gcc现在已经大胆地这么做了,它的-fstrict-aliasing
开关。而这也造成了一些问题。例如,请参阅关于Mysql bug的优秀文章http://davmac.wordpress.com/2009/10/,以及http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html中同样优秀的讨论。
其他一些不太相关的链接:
- performance-impact-of-fno-strict-aliasing
- strict-aliasing字符串
- when-is-char-safe-for-strict-pointer-aliasing
- how-to-detect-strict-aliasing-at-compile-time
重复一遍,你有自己的恐怖故事吗?当然,* 不是 * 由-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;
}
字符串
具体投诉内容为:
一些用户抱怨说,当编译[上述]代码时不使用-fno-strict-aliasing,write和memcpy的顺序会颠倒(这意味着一个伪造的len会被mem-copy到流中)。
编译代码,在CYGWIN wih -O3上使用gcc4.3.4(如果我错了,请纠正我--我的汇编程序有点生 rust 了!):
_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
型
对于迈克尔回答中的第二个环节
*(unsigned short *)&a = 4;
型
gcc通常会(总?)给予警告。但我 * 相信 * 对此(对于gcc)的有效解决方案是用途:
#define CAST(type, x) (((union {typeof(x) src; type dst;}*)&(x))->dst)
// ...
CAST(unsigned short, a) = 4;
型
我曾经问过SO在gcc-strict-aliasing-and-casting-through-a-union中是否可以这样做,但到目前为止没有人不同意。
6条答案
按热度按时间wi3ka0sx1#
没有我自己的恐怖故事,但这里有一些引用Linus Torvalds(抱歉,如果这些已经在问题中的链接引用之一):
http://lkml.org/lkml/2003/2/26/158:
日期2003年2月26日星期三09:22:15 -0800受试者回复:没有-fno-strict-aliasing的无效编译来自Jean Tourrilhes <>
On Wed,Feb 26,2003 at 04:38:10PM +0100,Horst von Brand wrote:
Jean Tourrilhes <>说道:
在我看来像是编译器的bug...一些用户抱怨说,当下面的代码在没有-fno-strict-aliasing的情况下编译时,write和memcpy的顺序被颠倒了(这意味着一个伪len被mem-copy到流中)。代码(来自linux/include/net/iw_handler.h):
字符串
恕我直言,编译器应该有足够的上下文来知道重新排序是危险的。任何建议,使这个简单的代码更防弹欢迎。
由于严格的别名,编译器可以自由地假设char *stream和struct iw_event *iwe指向不同的内存区域。
这是真的,这不是我抱怨的问题。
(Note后见之明:这段代码很好,但是Linux的
memcpy
实现是一个宏,它强制转换为long *
以进行更大的块复制。使用正确定义的memcpy
,不允许gcc -fstrict-aliasing
破坏此代码。但这意味着如果编译器不知道如何将字节复制循环转换为有效的asm,则需要内联asm或__attribute__((aligned(1),may_alias))
(e.g. in atypedef
)来定义内核memcpy
(gcc 7之前的gcc就是这种情况)Linus Torvald的评论:
Jean Tourrilhes jt@bougret.hpl.hp.com写道:
在我看来像是编译器的bug...
你认为内核为什么使用“-fno-strict-aliasing”?
gcc的人更感兴趣的是试图找出c99规范允许什么,而不是让事情真正 * 工作 *。特别是别名代码甚至不值得启用,它只是不可能理智地告诉gcc什么时候有些东西可以别名。
一些用户抱怨说,当下面的代码在没有-fno-strict-aliasing的情况下编译时,write和memcpy的顺序被颠倒了(这意味着一个伪len被mem-copy到流中)。
“问题”是我们内联了memcpy(),这时gcc不会关心它可以别名的事实,所以他们只会重新排序所有内容并声称这是我们自己的错。即使我们没有理智的方式告诉GCC这件事。
几年前,我试图找到一种合理的方法,但gcc的开发人员并不关心这个领域的现实世界。从我已经看到的回复来看,如果这种情况发生了变化,我会感到惊讶。
我不想和你争。
莱纳斯
http://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg01647.html:
基于类型的别名是愚蠢的。太愚蠢了,一点都不好笑。坏了GCC接受了这个破碎的概念,并通过使其成为一个毫无意义的“法律条文”来使其更加破碎。
...
我知道一个 * 事实 *,gcc将重新排序写访问,显然是(静态)相同的地址。Gcc会突然觉得
型
可以重新排序,首先将其设置为4(因为通过阅读标准,显然它们不会别名),然后因为现在'a=5'的赋值是稍后的,所以可以完全省略4的赋值!如果有人抱怨编译器是疯狂的,编译器的人会说“nyaah,nyaah,标准的人说我们可以做到这一点”,绝对没有反省是否有任何意义。
rjee0c152#
SWIG生成的代码依赖于关闭严格别名,这可能会导致各种问题。
字符串
js81xvg63#
**gcc、aliasing和二维变长数组:**以下示例代码复制了一个2x2矩阵:
字符串
在CentOS上使用gcc4.1.2,我得到:
型
我不知道这是否是众所周知的,我不知道这是一个错误还是一个功能。* 我无法在Cygwin* 上复制gcc4.3.4的问题,因此它可能已被修复。一些变通方法:
__attribute__((noinline))
作为copy()。-fno-strict-aliasing
。b[][n]
更改为b[][2]
。-O2
或-O3
。进一步说明:
copy()
。(顺便说一句,我有点惊讶地看到gcc没有展开双循环。-Wstrict-aliasing=
,在这里做任何事情。我向GCC Bugzilla报告了这个问题,但他们对旧的4.1.2不感兴趣,尽管(我相信)它是价值10亿美元的RHEL 5的关键。它不会发生在4.2.4以上。
我有一个稍微简单一点的类似bug的例子,只有一个矩阵。代码:
型
产生的结果:
型
似乎是
-fstrict-aliasing
和-finline
的组合导致了这个bug。js4nwp544#
以下是我的:
http://forum.openscad.org/CGAL-3-6-1-causing-errors-but-CGAL-3-6-0-OK-tt2050.html
它导致CAD程序中的某些形状被错误地绘制。谢天谢地,项目的领导者们致力于创建一个回归测试套件。
这个错误只在某些平台上表现出来,包括旧版本的GCC和旧版本的某些库。然后只打开-O2。-fno-strict-aliasing解决了这个问题。
p4tfgftt5#
C语言的公共初始序列规则曾经被解释为可以编写一个可以在各种结构类型的前导部分工作的函数,只要它们以匹配类型的元素开始。在C99中,该规则被更改,因此它仅适用于所涉及的结构类型是同一个联合的成员,其 complete 声明在使用时可见。
gcc的作者坚持认为,所讨论的语言只适用于通过union类型执行访问的情况,尽管事实是:
1.如果必须通过联合类型执行访问,则没有理由指定 complete 声明必须可见。
1.尽管CIS规则是根据联合来描述的,但它的主要用途在于它对结构的布局和访问方式的暗示。如果S1和S2是共享一个CIS的结构,那么一个从外部源接受指向S1和S2的指针的函数就不可能遵守C89的CIS规则,而不允许相同的行为对指向实际上不在联合对象内部的结构的指针有用;因此,规定CIS对结构的支持是多余的,因为已经为工会规定了这一点。
iqxoj9l96#
下面的代码在gcc 4.4.4下返回10。union方法或gcc 4.4.4有什么问题吗?
字符串