c – GCC,-O2和位域 – 这是一个bug还是一个功能?
今天我在实验了位域时发现了令人震惊的行为.为了讨论和简单,这里是一个示例程序:
#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”编译时,这是(正确的)输出:
以下是使用“g -O2”(和-O3)编译时的输出:
检查后一个例子的装配,我发现这个: 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):
在这种情况下,-O2会发生一个更有趣的事情:
我承认这个属性是有用的东西;在这种情况下,我关心的优化设置有所不同. 解决方法
如果你想获得技术,你使用的分钟是__attribute__(包含两个连续的下划线的标识符),你的代码有/没有定义的行为.
如果您与删除的操作相同,则会像编译器错误一样看起来像我. 3位字段被视为7的事实意味着它被视为无符号的,所以当你溢出时,它应该像任何其他无符号一样,并给出模数运算. 它将合法的方式视为已签署的位域.在这种情况下,第一个结果将是-1,-3或-0(可能打印为0),第二个未定义(因为有符号整数的溢出给出未定义的行为).理论上,其他值可能在C89或当前C标准下是可能的,因为它们不限制带符号整数的表示.在C99或C 0x中,它只能是这三个(C99限制对一个补码进行有符号整数,二进制补码或符号大小,C 0x基于C99而不是C90). 糟糕:我没有足够的关注 – 由于它被定义为无符号,它必须被视为无符号,留下一些摆脱摆脱编译器错误的空间. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |