web-dev-qa-db-ja.com

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

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

_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バイトのサイズを期待していたため、明確にする必要があります。

91
Raffaello

フィールドの基本型として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など.

95
didierc

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

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

(同じバイト内のフィールド/パディングの順序は意図的なものではなく、コンパイラがそれを好みの方法でレイアウトできるため、単にアイデアを提供することです)

78
Jack

最初の2ビットのフィールドは、単一のcharに収まります。 3番目はcharに収まらないため、新しいものが必要です。 3 + 3 + 3 = 9は、8ビット文字に適合しません。

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

16
2501

ほとんどのコンパイラでは、パディングを制御できます 例:#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
}

コンパイラのデフォルトの動作には理由があるため、パフォーマンスが向上する可能性があります。

15
Kos

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ビットにありますが、これらは下位または上位アドレス)。ただし、そのような機能がない場合、マクロはおそらくそのようなことを操作する唯一の移植可能な方法です。

9
supercat