今日、ビット フィールドを使って実験を行っているときに、驚くべき動作を発見しました。説明と単純化のために、ここにプログラム例を示します。
#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
属性を使用するのは怪しいものであることは認めます。この場合、私が懸念している最適化設定の違いです。