なぜこの構造体のサイズが2ではなく3なのですか?


91

私はこの構造体を定義しました:

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バイトであると予想していたので、説明が必要です。


4
それはおそらくアライメントの最適化です。次のビットサイズが実際の占有スペースに収まらない場合は、新しいバイトを開始します。
ῥεῖπάντα

4
ビットパッキングを必要とする外部制約がいくつかあり、プラットフォームが標準の提供するものに対していくつかの追加の保証を提供しない限り、ビットフィールドを使用する意味はほとんどありません。
デビッドロドリゲス-dribeas 14年

3
通知はCのために、文字を使用すると、int型を使用するよりも少ないポータブルであることstackoverflow.com/a/23987436/23118を
hlovdal 2014年

2
ビットフィールドに関するほとんどすべてが実装定義であることに注意してください。コンパイラによって答えが異なる場合があり、頼りになることはありません。また、signed charまたはを指定しなかったためunsigned char、コンパイラーがcharビットフィールドの「プレーン」を符号付きまたは符号なしとして扱うかどうかをドキュメントを見ないと判別できないため、決定は(理論的には) 'plain' charは、ビットフィールドの外部で使用される場合、符号付きまたは符号なしです。
ジョナサンレフラー2014年

3
具体的には、C99、§6.7.2.1構造体と組合指定子に、¶4 ビットフィールドは、修飾または非修飾バージョンである型を持たなければならない_Boolsigned intunsigned int、またはいくつかの他の実装定義型。charしたがって、 使用は「その他の実装定義型」カテゴリに分類されます。
Jonathan Leffler、2014年

回答:


95

charフィールドの基本型として使用しているため、コンパイラはビットごとにビットをグループ化しようとします。各バイトに8ビットを超えることはできないため、バイトごとに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番目の構造体の歩留まりになります。aよりも大きい場合short、構造体は使用されたタイプの要素を1つしか埋めないため、同じプラットフォームの場合、構造体のサイズはで4、intで8 longなどになります。


1
提案された構造体の定義はまだ間違っています。正しい構造体の定義では「unsigned short」を使用します。
user3629249 14年

21
@ user3629249 unsigned shortが「正しい」のはなぜですか?ユーザーが-4から3までを保存する場合は、shortが適切です。ユーザーが0から7までを保管したい場合は、unsigned shortが適切です。元の質問は署名付きのタイプを使用していましたが、それが意図的なものか偶発的なものかはわかりません。
Bruce Dawson

2
なぜbeetween charとに違いがあるのshortですか?
GingerPlusPlus

5
@BruceDawson:標準により、実装はchar署名されていないことが許可されます...
Thomas Eding、2014年

@ThomasEding True、標準では、charに署名を付けることを許可しています。しかし、私の主なポイントは残っています。署名されていないshortが正しいと主張する理由は(通常はそうですが)与えられなかったことです。
Bruce Dawson

78

最小整列境界(1バイト)にまたがるビットパケットフィールドを持つことができないため、おそらく次のようにパックされます。

byte 1
  A : 3
  B : 3
  padding : 2
byte 2
  C : 3
  D : 3
  padding : 2
byte 3
  E : 3
  padding : 5

(同じバイト内のフィールド/パディングの順序は意図的ではありません。コンパイラーが好みに応じてそれらを配置できるため、それは単にアイデアを与えるためです)


16

最初の2つのビットフィールドは1つに収まりcharます。3番目はそれに適合できずchar、新しいものが必要です。3 + 3 + 3 = 9で、8ビット文字に収まりません。

したがって、最初のペアはを受け取りchar、2番目のペアはを受け取りchar、最後のビットフィールドは3番目を受け取りcharます。


15

ほとんどのコンパイラでは、たとえば#pragmas を使用して、パディングを制御できます。以下は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
}

コンパイラーのデフォルトの動作は理由があるため、おそらくパフォーマンスが向上することに注意してください。


9

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 char8ビットで、コンパイラは、そのタイプの四つのフィールドを割り当て、及び(にされるであろう全てが、1〜2ビットフィールドを割り当てるために必要とされるchar独自の分野)。すべてのchar宣言がに置き換えられた場合short、タイプの2つのフィールドがありshort、1つは5つのビットフィールドを保持し、もう1つは残りの2つを保持します。

アラインメント制限のないプロセッサunsigned shortでは、最初の5つのフィールドに使用unsigned charし、最後の2つのフィールドに7つの3ビットフィールドを3バイトで格納することにより、データをより効率的にレイアウトできます。3つのバイトに8つの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;

このような構文が許可されている場合は、コードがビットサイズを移植可能な方法で使用できるようになります。ワードサイズやバイト順は関係ありません(foo0はf1の最下位3ビットにありますが、これらは下位または上位のアドレス)。ただし、そのような機能がない場合、マクロはおそらくそのようなものを操作する唯一の移植可能な方法です。


2
コンパイラが異なれば、ビットフィールドのレイアウトも異なります。Visual C ++が関連性のある方法で行う方法についてのドキュメントをいくつか書きました。それはいくつかの迷惑な落とし穴を指摘しています: randomascii.wordpress.com/2010/06/06/…–
Bruce Dawson

さて、あなたは通常のタイプのストアと同等のものを言っており、ビットフィールド演算子を使用して、関心のある単一の変数を達成し、このメカニズムを簡素化するためにいくつかのマクロを使用します。私はc / c ++で生成されたコードもこのようなことをしていると思います。構造体を使用することは、コードを「よりよく」整理するためだけのものであり、実際にはまったく必要ありません。
Raffaello
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.