c – GCC,-O2和位域 – 这是一个bug还是一个功能?

今天我在实验了位域时发现了令人震惊的行为.为了讨论和简单,这里是一个示例程序:
#include <stdio.h>

struct Node
{
  int a:16 __attribute__ ((packed));
  int b:16 __attribute__ ((packed));

  unsigned int c:27 __attribute__ ((packed));
  unsigned int d:3 __attribute__ ((packed));
  unsigned int e:2 __attribute__ ((packed));
};

int main (int argc,char *argv[])
{
  Node n;
  n.a = 12345;
  n.b = -23456;
  n.c = 0x7ffffff;
  n.d = 0x7;
  n.e = 0x3;

  printf("3-bit field cast to int: %d\n",(int)n.d);

  n.d++;  

  printf("3-bit field cast to int: %d\n",(int)n.d);
}

该程序有意导致3位位字段溢出.当使用“g -O0”编译时,这是(正确的)输出

3-bit field cast to int: 7

3-bit field cast to int: 0

以下是使用“g -O2”(和-O3)编译时的输出

3-bit field cast to int: 7

3-bit field cast to int: 8

检查后一个例子的装配,我发现这个:

movl    $7,%esi
movl    $.LC1,%edi
xorl    %eax,%eax
call    printf
movl    $8,%eax
call    printf
xorl    %eax,%eax
addq    $8,%rsp

优化刚刚插入“8”,假设7 1 = 8,其实数字溢出并且为零.

幸运的是,我关心的代码不会像我所知道的那样溢出,但是这种情况吓倒了我 – 这是一个已知的bug,一个功能,还是这个预期的行为?我什么时候可以期待gcc是正确的?

编辑(re:signed / unsigned):

它被视为unsigned,因为它被声明为unsigned.将其声明为int,得到输出(使用O0):

3-bit field cast to int: -1

3-bit field cast to int: 0

在这种情况下,-O2会发生一个更有趣的事情:

3-bit field cast to int: 7

3-bit field cast to int: 8

我承认这个属性是有用的东西;在这种情况下,我关心的优化设置有所不同.

解决方法

如果你想获得技术,你使用的分钟是__attribute__(包含两个连续的下划线的标识符),你的代码有/没有定义的行为.

如果您与删除的操作相同,则会像编译器错误一样看起来像我. 3位字段被视为7的事实意味着它被视为无符号的,所以当你溢出时,它应该像任何其他无符号一样,并给出模数运算.

它将合法的方式视为已签署的位域.在这种情况下,第一个结果将是-1,-3或-0(可能打印为0),第二个未定义(因为有符号整数的溢出给出未定义的行为).理论上,其他值可能在C89或当前C标准下是可能的,因为它们不限制带符号整数的表示.在C99或C 0x中,它只能是这三个(C99限制对一个补码进行有符号整数,二进制补码或符号大小,C 0x基于C99而不是C90).

糟糕:我没有足够的关注 – 由于它被定义为无符号,它必须被视为无符号,留下一些摆脱摆脱编译器错误的空间.

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...