gcc-strict-aliasing-and-casting-through-a-union年,我问过是否有人遇到过unions 通过指针双关语的问题.到目前为止,答案似乎是No.

这个问题更广泛:你有any个关于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-aliasingswitch .这导致了一些问题.例如,请参阅关于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:Michael Burr's answer中的第一个链接,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;
}

具体投诉如下:

一些用户抱怨说,当编译[上述]代码时没有-fno严格的别名,写入和memcpy的顺序是颠倒的(这意味着伪len是mem复制到流中的).

编译代码,在CYGWIN with h-O3上使用gcc 4.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通常(总是?)给个警告.但I believe对此(对于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年后这是否可以,但到目前为止没有人不同意.

推荐答案

我自己没有恐怖故事,但以下是莱纳斯·托瓦尔兹(莱纳斯 Torvalds)的一些引语(如果这些引语已经出现在问题中的一个链接参考文献中,那就很抱歉):

http://lkml.org/lkml/2003/2/26/158:

Date Wed, 26 Feb 2003 09:22:15 -0800 Subject Re: Invalid compilation without -fno-strict-aliasing From Jean Tourrilhes <>

2003年2月26日星期三下午4:38:10+0100时,霍斯特·冯·布兰德写道:

Jean Tourrilhes <> said:

对我来说它看起来像是一个编译器错误. 一些用户抱怨说,当以下代码 在没有-fno-strict别名的情况下编译,写入的顺序和 memcpy是反转的(这意味着伪造的镜头被临时复制到 流). 代码(来自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 point

这是真的,这不是我抱怨的问题.

(事后诸葛亮:这段代码很好,但Linux的memcpy was a macro that cast to long *实现需要在更大的块中复制.对于正确定义的memcpygcc -fstrict-aliasing不允许 destruct 这段代码.但这意味着如果编译器不知道如何将字节复制循环转化为高效的asm,则需要内联asm来定义内核memcpy,gcc在gcc7之前就是这种情况)

以及莱纳斯·托瓦尔德(莱纳斯 Torvald)对上述内容的 comments :

Jean Tourrilhes wrote: >

在我看来,这像是一个编译器错误...

你认为内核为什么使用"-fno严格别名"?

海湾合作委员会的人更感兴趣的是试图找出可能发生的事情

一些用户抱怨说,当以下代码 在没有-fno-strict别名的情况下编译,写入的顺序和 memcpy是反转的(这意味着伪造的镜头被临时复制到 流).

"问题"在于我们内联了memcpy(),在这一点上GCC不会 关心它会有别名的事实,所以他们会重新订购 把一切都说成是自己的错.即使没有理智的人 我们竟然可以把这件事告诉GCC.

几年前,我试图找到一种理智的方法,而gcc开发人员真的

我不想费心go 反抗.

莱纳斯

http://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg01647.html:

基于类型的锯齿为stupid.这太愚蠢了,甚至一点都不好笑.它坏了.GCC接受了这个破碎的概念,把它变成了一个毫无意义的"法律条文",从而使它变得更加如此.

...

我知道对于fact%,GCC会对明显(静态地)相同地址的写访问进行重新排序.GCC会突然想到,

unsigned long a;

a = 5;
*(unsigned short *)&a = 4;

可能会被重新排序,先将其设置为4(因为通过阅读标准,他们显然没有别名),然后因为现在"a=5"的赋值是后来的,所以4的赋值可以完全省略!如果有人抱怨编译器疯了,编译器的人会说"Nyah,Nyah,人们说我们可以做到这一点的标准",而完全没有反省,问它是否有意义.

C++相关问答推荐

librsvg rsvg_handle_get_dimensions获取像素大小与浏览器中的渲染大小没有不同

当main函数调用被重构时,C函数给出错误的结果

在使用GTK 4 Columnview列表模型时,如何为多列添加排序函数.C编码,Linux/GNOME环境

MISRA C:2012 11.3违规强制转换(FLOAT*)到(uint32_t*)

增加getaddrinfo返回的IP地址数量

为什么在此程序中必须使用Volatile关键字?

在C语言中,在数学运算过程中,为什么浮点数在变量中的行为不同

函数内的局部字符指针

对于C中给定数组中的每个查询,如何正确编码以输出给定索引范围(1到N)中所有数字的总和?

如何使解释器存储变量

从uint8_t*转换为char*可接受

如何用C语言为CLI应用程序编写按键检测系统?

使用%f格式说明符打印整数值

我在C程序的Flex/Bison中遇到语法错误

pthread_create的用法

为什么GCC-O1优化破解了这个代码,为了一个GameBoy高级只读存储器而修改了VRAM的循环?

GetText不适用于包含国际字符的帐户名称

可以';t从A9G模块拨打电话

模仿 memmove 的行为

C11 嵌套泛型