ビット フィールドのほぼすべての側面は実装定義です。「プレーンなint
」ビットフィールドの符号でさえ実装定義です。署名されている場合と署名されていない場合があります。フィールドのレイアウト — それらが含まれる「ユニット」(標準で使用される用語) の最上位ビットから最下位ビットに移動するか、最下位ビットから最上位ビットに移動するかは、実装によって定義されます。最大許容ビット フィールドのサイズ。ビットフィールドが新しいユニットに格納されるとき。これらはすべて実装定義です。
たとえば、GCC 4.8.1 を使用する Mac OS X 10.8.4 ではstruct bitfield
、問題の がa
最下位 3 ビット (ビット 0 ~ 2) をb
占め、次の 13 ビット (3 -15)、c
次の 1 ビット (16) を占めます。
#include <stdio.h>
static void print_info(int v);
int main(void)
{
int values[] =
{
0x55555555, 0xAAAAAAAA, 0x87654321, 0xFEDCBA98,
0xFEDCBA90, 0xFEDCBA91, 0xFEDCBA92, 0xFEDCBA93,
0xFEDCBA94, 0xFEDCBA95, 0xFEDCBA96, 0xFEDCBA97,
0xFEDCBA98, 0xFEDCBAA0, 0xFEDCBAA8, 0x0000BAA0,
0x0001BAA0, 0x00000008, 0x00000010, 0x00000018,
0x0000FFF0, 0x0000FFF8,
};
for (size_t i = 0; i < sizeof(values)/sizeof(values[0]); i++)
print_info(values[i]);
return 0;
}
static void print_info(int v)
{
union
{
unsigned int x;
struct bitfield
{
signed int a:3;
unsigned int b:13;
unsigned int c:1;
} y;
} u;
u.x = v;
printf("0x%.8X => %2d 0x%.4X %1X\n", u.x, u.y.a, u.y.b, u.y.c);
}
出力例:
0x55555555 => -3 0x0AAA 1
0xAAAAAAAA => 2 0x1555 0
0x87654321 => 1 0x0864 1
0xFEDCBA98 => 0 0x1753 0
0xFEDCBA90 => 0 0x1752 0
0xFEDCBA91 => 1 0x1752 0
0xFEDCBA92 => 2 0x1752 0
0xFEDCBA93 => 3 0x1752 0
0xFEDCBA94 => -4 0x1752 0
0xFEDCBA95 => -3 0x1752 0
0xFEDCBA96 => -2 0x1752 0
0xFEDCBA97 => -1 0x1752 0
0xFEDCBA98 => 0 0x1753 0
0xFEDCBAA0 => 0 0x1754 0
0xFEDCBAA8 => 0 0x1755 0
0x0000BAA0 => 0 0x1754 0
0x0001BAA0 => 0 0x1754 1
0x00000008 => 0 0x0001 0
0x00000010 => 0 0x0002 0
0x00000018 => 0 0x0003 0
0x0000FFF0 => 0 0x1FFE 0
0x0000FFF8 => 0 0x1FFF 0
テスト値は完全に無作為に選択されるわけではありません。テスト値 0xFEDCBA90 ~ 0xFECBA97 から、最下位 3 ビットに が含まれていることがわかりますa
。テスト値 0x0000BAA0 と 0x0001BAA0 から、17 番目のビット (またはビット 16) に が含まれていることがわかりますc
。そして、テスト値 0x00000008 から 0x0000FFF8 まで、ビット 3-15 にb
.
ただし、このコードは理論的には議論の余地があるほど移植可能であることを指摘しておく必要があります。コードは に書き込み、次におよびをu.x
読み取るため、最後に書き込まれた共用体のメンバーにアクセスしていません。これは厳密には未定義の動作です。実際には、「常に」機能します (機能しないシステムについては聞いたことがありません。機能しないシステムがある可能性は低いですが、技術的に不可能ではありません)。u.x
u.y.a
u.y.b
u.y.c
このレイアウトは、想像力を駆使して可能な唯一のレイアウトではありません。ただし、別のレイアウトを示すコンパイラやシステムにアクセスすることはできません。
ISO/IEC 9899:2011 のセクション§6.7.2.1 構造体と共用体の指定子には、次のように記載されています。
¶11 実装は、ビットフィールドを保持するのに十分な大きさのアドレス指定可能なストレージユニットを割り当てることができます。十分なスペースが残っている場合、構造内の別のビットフィールドの直後に続くビットフィールドは、同じユニットの隣接するビットにパックされます。十分なスペースが残っていない場合、収まらないビットフィールドを次のユニットに配置するか、隣接するユニットとオーバーラップするかは実装定義です。ユニット内のビットフィールドの割り当て順序 (上位から下位、または下位から上位) は実装定義です。アドレス可能なストレージ ユニットのアラインメントは指定されていません。
¶12 宣言子がなく、コロンと幅のみのビットフィールド宣言は、名前のないビットフィールドを示します。126)特殊なケースとして、幅が 0 のビットフィールド構造体メンバーは、以前のビットフィールドが配置されていた場合、それ以上のビットフィールドがユニットにパックされないことを示します。
126)名前のないビットフィールド構造体メンバーは、外部から課されたレイアウトに適合するパディングに役立ちます。
問題の構造のわずかな変形は次のとおりです。
struct exegesis
{
signed int a:3;
unsigned int :0;
unsigned int b:13;
unsigned int :0;
unsigned int c:1;
};
この構造体のサイズは 12 です (以前と同じコンパイラ/プラットフォーム上で)。このプラットフォームのビット フィールドのストレージ ユニットは 4 バイトであるため、匿名のゼロ幅フィールドは新しいストレージ ユニットを開始します。 a
最初の 4 バイト単位の下位 3 ビットに格納されます。b
2 番目の 4 バイト単位の最下位 13 ビット。3 番目の 4 バイト単位のc
最下位ビット。標準からの引用で述べたように、0 より大きい匿名ビット フィールドも使用できます。