O2和位域

今天,我在试验比特场时发现了令人震惊的行为。 为了讨论和简单起见,下面是一个示例程序:

#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: %dn",(int)n.d);

  n.d++;  

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

该程序故意导致3位位域溢出。 这是使用“g ++ -O0”编译时的(正确)输出:

3位字段强制转换为int:7

3位字段强制转换为int:0

这是使用“g ++ -O2”(和-O3)编译时的输出:

3位字段强制转换为int:7

3位字段强制转换为int:8

检查后一个例子的程序集,我发现这个:

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

优化刚插入“8”,假设7 + 1 = 8,实际上数字溢出且为零。

幸运的是,我所关心的代码并没有像我所知的那样溢出,但是这种情况让我感到害怕 - 这是一个已知的错误,一个特性,还是这种预期的行为? 我什么时候可以期待gcc对此有所了解?

编辑(重新签名/未签名):

它被视为未签名,因为它被声明为无符号。 声明它为int你得到输出(与O0):

3位字段强制转换为int:-1

3位字段强制转换为int:0

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

3位字段强制转换为int:7

3位字段强制转换为int:8

我承认这个属性是一个可憎的东西, 在这种情况下,这是我关心的优化设置的差异。


如果你想获得技术,你使用__attribute__ (一个包含两个连续下划线的标识符)的那一刻,你的代码就会有/未定义的行为。

如果你对那些被删除的行为有相同的行为,它就像是一个编译器错误。 3位字段被视为7意味着它被视为无符号的事实,所以当你溢出它应该像其他任何未签名的,并给你模运算。

将位字段视为已签名也是合法的。 在这种情况下,第一个结果将是-1-3-0 (可能只打印0 ),第二个未定义(因为有符号整数的溢出会导致未定义的行为)。 理论上,在C89或当前的C ++标准下,其他值可能是可能的,因为它们不限制有符号整数的表示。 在C99或C ++ 0x中,它只能是那三个(C99限制有符号整数到补码,二进制补码或符号幅度,C ++ 0x是基于C99而不是C90)。

糟糕:我没有给予足够的关注 - 因为它被定义为unsigned ,所以它必须被视为unsigned ,为摆脱它的编译器错误留下一点回旋余地。

链接地址: http://www.djcxy.com/p/73545.html

上一篇: O2, and bitfields

下一篇: Purpose of Unions in C and C++