10

今日、ビット フィールドを使って実験を行っているときに、驚くべき動作を発見しました。説明と単純化のために、ここにプログラム例を示します。

#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」を使用してコンパイルした場合の(正しい)出力は次のとおりです。

int にキャストされた 3 ビット フィールド: 7

int にキャストされた 3 ビット フィールド: 0

"g++ -O2" (および -O3) を使用してコンパイルした場合の出力は次のとおりです。

int にキャストされた 3 ビット フィールド: 7

int にキャストされた 3 ビット フィールド: 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

実際には数値がオーバーフローしてゼロであるのに、7+1=8 であると仮定して、最適化により「8」が挿入されました。

幸いなことに、私が知る限り、関心のあるコードはオーバーフローしていませんが、この状況は私を怖がらせます。これは既知のバグなのか、機能なのか、それとも予想される動作なのか? これについてgccが正しいと期待できるのはいつですか?

編集 (re: 署名済み/未署名) :

署名なしと宣言されているため、署名なしとして扱われています。int として宣言すると、出力が得られます (O0 を使用):

int にキャストされた 3 ビット フィールド: -1

int にキャストされた 3 ビット フィールド: 0

この場合、-O2 を使用すると、さらに面白いことが起こります。

int にキャストされた 3 ビット フィールド: 7

int にキャストされた 3 ビット フィールド: 8

属性を使用するのは怪しいものであることは認めます。この場合、私が懸念している最適化設定の違いです。

4

1 に答える 1

8

技術的なことを知りたい場合は、使用した分__attribute__(2 つの連続したアンダースコアを含む識別子) のコードに未定義の動作がある/あった。

それらを削除しても同じ動作が得られる場合は、コンパイラのバグのように見えます。3 ビット フィールドが符号なしとして扱われているという事実は7、それが符号なしとして扱われていることを意味するため、オーバーフローすると、他の符号なしと同じように動作し、モジュロ演算が得られます。

ビットフィールドを署名済みとして扱うことも正当です。この場合、最初の結果は-1,-3または-0(単に と出力される可能性があります0) になり、2 番目の結果は未定義になります (符号付き整数のオーバーフローにより未定義の動作が発生するため)。理論的には、C89 または現在の C++ 標準では、符号付き整数の表現を制限しないため、他の値が可能である可能性があります。C99 または C++0x では、これらの 3 つしかありません (C99 は符号付き整数を 1 の補数、2 の補数、または符号の大きさに制限し、C++0x は C90 ではなく C99 に基づいています)。

おっと:私は十分に注意を払っていませんでした. として定義されunsignedているため、 として扱われる必要がunsignedあり、コンパイラのバグから抜け出すための余地がほとんどありません.

于 2010-05-14T19:44:37.470 に答える