![](/img/trans.png)
[英]Feature, bug or UB? The expected infinite loop in my code is deleted when using 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.