今天,我在尝试位域时发现了令人震惊的行为。为了讨论和简单起见,这里有一个示例程序:
#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
我承认使用属性是一件可疑的事情;在这种情况下,这是我担心的优化设置的差异。