Roo*_*oke 10 c c++ optimization gcc bit-fields
今天,我在尝试使用位字段时发现了令人担忧的行为.为了便于讨论和简化,这是一个示例程序:
#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);
}
Run Code Online (Sandbox Code Playgroud)
该程序故意导致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
Run Code Online (Sandbox Code Playgroud)
优化刚刚插入"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,留下一点摆动它摆脱编译器错误的余地.