web-dev-qa-db-ja.com

C構造体のパックされたビットフィールド-GCC

Linuxのcで構造体を使用しています。私はビットフィールドと "packed"属性を使い始め、奇妙な動作に遭遇しました。

struct t1
{
    int a:12;
    int b:32;
    int c:4;
}__attribute__((packed));

struct t2
{
    int a:12;
    int b;
    int c:4;
}__attribute__((packed));

void main()
{
    printf("%d\n",sizeof(t1)); //output - 6
    printf("%d\n",sizeof(t2)); //output - 7
}

なぜ両方の構造-まったく同じ-はバイト数が異なるのですか?

18
Danny Cohen

あなたの構造は「まったく同じ」ではありません。最初のビットフィールドには3つの連続するビットフィールドがあり、2番目のビットフィールドには1つのビットフィールド、(非ビットフィールド)int、そして2番目のビットフィールドがあります。

これは重要です:連続する(ゼロ以外の幅の)ビットフィールドは単一のメモリ位置にマージされますが、ビットフィールドの後に非ビットフィールドが続くと、別個のメモリ位置になります。

最初の構造には1つのメモリロケーションがあり、2番目の構造には3つのメモリロケーションがあります。最初の構造体ではなく、2番目の構造体でbメンバーのアドレスを取得できます。 bメンバーへのアクセスは、2番目の構造体のaまたはcへのアクセスと競合しませんが、最初の構造体ではアクセスします。

ビットフィールドメンバーがsensでそれを「閉じた」直後に非ビットフィールド(または長さがゼロのビットフィールド)があると、異なる/独立したメモリロケーション/オブジェクトになります。コンパイラーは、最初の構造体のようにビットフィールド内にbメンバーを「パック」できません。

29
Mat
struct t1 // 6 bytes
{
    int a:12; // 0:11
    int b:32; // 12:43
    int c:4;  // 44:47
}__attribute__((packed));

struct t1 // 7 bytes
{
    int a:12; // 0:11
    int b;    // 16:47
    int c:4;  // 48:51
}__attribute__((packed));

通常のint bは、バイト境界に揃える必要があります。その前にパディングがあります。 caのすぐ隣に置くと、このパディングは不要になります。 int b:32のようなバイト境界以外の整数へのアクセスは遅いため、おそらくこれを行う必要があります。

18
John Zwinck