#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
我承认这个属性是有用的东西;在这种情况下,我关心的优化设置有所不同.
解决方法
如果您与删除的操作相同,则会像编译器错误一样看起来像我. 3位字段被视为7的事实意味着它被视为无符号的,所以当你溢出时,它应该像任何其他无符号一样,并给出模数运算.
它将合法的方式视为已签署的位域.在这种情况下,第一个结果将是-1,-3或-0(可能打印为0),第二个未定义(因为有符号整数的溢出给出未定义的行为).理论上,其他值可能在C89或当前C标准下是可能的,因为它们不限制带符号整数的表示.在C99或C 0x中,它只能是这三个(C99限制对一个补码进行有符号整数,二进制补码或符号大小,C 0x基于C99而不是C90).
糟糕:我没有足够的关注 – 由于它被定义为无符号,它必须被视为无符号,留下一些摆脱摆脱编译器错误的空间.