c++ - GCC、-O2 和位域——这是错误还是功能?

标签 c++ c gcc bit-fields optimization

今天我在试验位域时发现了令人震惊的行为。为了讨论和简单起见,这里有一个示例程序:

#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, %esi
movl    $.LC1, %edi
xorl    %eax, %eax
call    printf
xorl    %eax, %eax
addq    $8, %rsp

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

幸运的是,据我所知,我关心的代码没有溢出,但这种情况让我感到害怕——这是一个已知的错误、一个功能,还是这是预期的行为?我什么时候可以期望 gcc 在这方面是正确的?

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

它被视为未签名,因为它被声明为未签名。将其声明为 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 使用起来有点可疑;在这种情况下,我关心的是优化设置的差异。

最佳答案

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

如果你在删除那些后得到相同的行为,在我看来它就像一个编译器错误。一个 3 位字段被视为 7 的事实意味着它被视为一个无符号的,所以当你溢出时它应该像任何其他无符号的一样,并给你模算术。

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

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

关于c++ - GCC、-O2 和位域——这是错误还是功能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2836978/

相关文章:

c++ - Apple C 模块进入 Xcode 工具链是否有时间表?

c++ - "address of array"的实际用例是什么?

c++ - 使用 gradle native 构建失败

linux - 在远程Linux服务器上运行本地GCC

gcc - gcc 为编译器版本号预定义的宏是什么?

gcc - 忽略 cpp 中的无效指令

c++ - 为什么 BGL A* 需要隐式图来建模 VertexListGraph?

c - BYTE、WORD 和 DWORD 宏定义

c - 从文件读取到链表 C

c - _mm256_slli_si256 : error "last argument must be an 8-bit intermediate"