2010-05-14 52 views
10

今天我与位域实验时发现了令人震惊的行为。为了讨论和简单起见,下面是一个示例程序:GCC,-02,和位域 - 这是一个错误或功能?

#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

这里的使用 “克++ -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时实际上溢出次数并且是零。

幸好我在乎据我所知不会溢出,但这种情况让我害怕的代码 - 这是一个已知的bug,功能,或者这是预期的行为?我什么时候可以期待gcc对此有所了解?

编辑(RE:符号/无符号):

,因为它被声明为unsigned它被视为无符号。声明为INT你得到的输出(带O0):

3位字段强制转换为int:-1

3位字段强制转换为int:0

一个更有趣的事情与-O2发生在这种情况下:

3位字段浇铸到int:7

3位字段浇铸到int:8

我承认属性是一个可腥的东西使用;在这种情况下,这是我关心的优化设置的差异。

+1

chedcked gcc 4.4.1 - 输出是7/0有/无优化 – dimba 2010-05-14 19:33:30

+0

我承认我使用4.1.2 - 谢谢你的提升。 – Rooke 2010-05-14 19:39:41

+0

如果在整个结构之后放置__attribute __((__ packed __)),会发生什么? – 2010-05-14 19:48:52

回答

8

如果你想获得的技术,你使用的分钟__attribute__(包含两个连续的下划线标识)您的代码/有不确定的行为。

如果你得到去除那些相同的行为,这在我看来就像一个编译器错误。这3位字段被视为7,这意味着它被视为无符号,所以当你溢出它应该做的像任何其他未签名的,给你模运算。

它将位字段视为有符号也是合法的。在这种情况下,第一个结果将是-1-3-0(可能只打印0),第二个未定义(因为有符号整数的溢出会导致未定义的行为)。理论上,在C89或当前的C++标准下,其他值可能是可能的,因为它们不限制有符号整数的表示。在C99或C++ 0x中,它只能是那三个(C99限制有符号整数到补码,二进制补码或符号幅度,C++ 0x是基于C99而不是C90)。

糟糕:我没有给予足够的关注 - 因为它被定义为unsigned,它必须被视为unsigned,为摆脱它的编译器错误留下一点回旋余地。

+0

更好的回答,+1 – WhirlWind 2010-05-14 19:52:08

+0

删除__attribute __也给出错误的行为-O2,但不是O0(按照原始代码)。看起来我需要使用gcc 4.4! – Rooke 2010-05-18 18:19:35

相关问题