この構造体を定義しました:
_typedef struct
{
char A:3;
char B:3;
char C:3;
char D:3;
char E:3;
} col;
_
sizeof(col)
は3を出力しますが、2であってはいけませんか? 1つの要素だけをコメントすると、sizeof
は2です。理由はわかりません。3ビットの5つの要素が15ビットに等しく、それが2バイト未満です。
このような構造を定義する際に「内部サイズ」はありますか?私はこれまでの言語の概念から、3バイトではなく2バイトのサイズを期待していたため、明確にする必要があります。
フィールドの基本型としてchar
を使用しているため、コンパイラはバイト単位でビットをグループ化しようとします。また、各バイトに8ビットを超えることはできないため、1バイトにつき2フィールドしか格納できません。
構造体が使用するビットの合計は15なので、その量のデータに適合する理想的なサイズはshort
になります。
#include <stdio.h>
typedef struct
{
char A:3;
char B:3;
char C:3;
char D:3;
char E:3;
} col;
typedef struct {
short A:3;
short B:3;
short C:3;
short D:3;
short E:3;
} col2;
int main(){
printf("size of col: %lu\n", sizeof(col));
printf("size of col2: %lu\n", sizeof(col2));
}
上記のコード(私のような64ビットプラットフォームの場合)は、実際に2
2番目の構造体。 short
よりも大きい場合、構造体は使用される型の要素を1つだけ埋めます。そのため、その同じプラットフォームでは、構造体はint
のサイズ4、8 long
など.
最小のアライメント境界(1バイト)にまたがるビットパケットフィールドを持つことはできないため、おそらく次のようにパックされます。
byte 1
A : 3
B : 3
padding : 2
byte 2
C : 3
D : 3
padding : 2
byte 3
E : 3
padding : 5
(同じバイト内のフィールド/パディングの順序は意図的なものではなく、コンパイラがそれを好みの方法でレイアウトできるため、単にアイデアを提供することです)
最初の2ビットのフィールドは、単一のchar
に収まります。 3番目はchar
に収まらないため、新しいものが必要です。 3 + 3 + 3 = 9は、8ビット文字に適合しません。
したがって、最初のペアはchar
を取り、2番目のペアはchar
を取り、最後のビットフィールドは3番目のchar
を取ります。
ほとんどのコンパイラでは、パディングを制御できます 例:#pragma
s 。 GCC 4.8.1の例を次に示します。
#include <stdio.h>
typedef struct
{
char A:3;
char B:3;
char C:3;
char D:3;
char E:3;
} col;
#pragma pack(Push, 1)
typedef struct {
char A:3;
char B:3;
char C:3;
char D:3;
char E:3;
} col2;
#pragma pack(pop)
int main(){
printf("size of col: %lu\n", sizeof(col)); // 3
printf("size of col2: %lu\n", sizeof(col2)); // 2
}
コンパイラのデフォルトの動作には理由があるため、パフォーマンスが向上する可能性があります。
ANSI C規格では、「コンパイラはビットフィールドを適切にパックすることが許可されている」という利点を提供するためにビットフィールドをどのようにパックするかについてほとんど規定していませんが、それでも多くの場合、コンパイラが最も効率的な方法で物事をパックすることを禁止しています。
特に、構造体にビットフィールドが含まれる場合、コンパイラは、「通常の」ストレージタイプの1つ以上の匿名フィールドを含む構造体として構造体を格納し、各フィールドを構成ビットフィールド部分に論理的に分割する必要があります。したがって、与えられた:
unsigned char foo1: 3;
unsigned char foo2: 3;
unsigned char foo3: 3;
unsigned char foo4: 3;
unsigned char foo5: 3;
unsigned char foo6: 3;
unsigned char foo7: 3;
unsigned char
が8ビットの場合、コンパイラはそのタイプの4つのフィールドを割り当て、1つを除くすべてに2つのビットフィールドを割り当てる必要があります(独自のchar
フィールドにあります)。すべてのchar
宣言がshort
に置き換えられた場合、タイプshort
の2つのフィールドがあり、そのうちの1つは5つのビットフィールドを保持し、もう1つは残りの2つ。
アライメント制限のないプロセッサーでは、最初の5つのフィールドにunsigned short
を使用し、最後の2つのフィールドにunsigned char
を使用して、3バイトで7つの3ビットフィールドを保存することで、データをより効率的にレイアウトできます。 8つの3ビットフィールドを3バイトで格納できるはずですが、コンパイラは、「外部フィールド」型として使用できる3バイトの数値型が存在する場合にのみ許可できます。
個人的には、定義されているビットフィールドは基本的に役に立たないと考えています。コードがバイナリパックされたデータを処理する必要がある場合、実際の型の格納場所を明示的に定義し、マクロまたはその他の手段を使用してそのビットにアクセスする必要があります。 Cが次のような構文をサポートしていると便利です。
unsigned short f1;
unsigned char f2;
union foo1 = f1:0.3;
union foo2 = f1:3.3;
union foo3 = f1:6.3;
union foo4 = f1:9.3;
union foo5 = f1:12.3;
union foo6 = f2:0.3;
union foo7 = f2:3.3;
このような構文が許可されている場合、コードは、Wordのサイズやバイトの順序に関係なく、コードが移植可能な方法でビットフィールドを使用できるようにします(foo0はf1の下位3ビットにありますが、これらは下位または上位アドレス)。ただし、そのような機能がない場合、マクロはおそらくそのようなことを操作する唯一の移植可能な方法です。