繁体   English   中英

GCC,-O2和位域-这是错误还是功能?

[英]GCC, -O2, and bitfields - is this a bug or a feature?

今天,我在尝试使用位字段时发现了令人震惊的行为。 为了讨论和简化起见,下面是一个示例程序:

#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位字段强制转换为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 ,为摆脱其成为编译器错误的余地留有余地。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM