7

まず、 2011 ISO C 規格のセクション 6.7.2.1のN1570ドラフトを引用して、ISO C 規格がビット フィールドについて述べていることを次に示します。

ビットフィールドは、 、 、 、またはその他の実装定義型の修飾または非修飾バージョンである型を持つものと_Boolsigned intますunsigned int。アトミック型が許可されるかどうかは実装定義です。

...

ビットフィールドは、指定されたビット数で構成される符号付きまたは符号なしの整数型を持つものとして解釈されます。値 0 または 1 が非ゼロ幅の type のビットフィールドに格納されている_Bool場合、ビットフィールドの値は格納されている値と等しくなります。_Bool ビットフィールドには のセマンティクスがあります_Bool

実装では、ビットフィールドを保持するのに十分な大きさのアドレス指定可能なストレージユニットを割り当てることができます。十分なスペースが残っている場合、構造内の別のビットフィールドの直後に続くビットフィールドは、同じユニットの隣接するビットにパックされます。十分なスペースが残っていない場合、収まらないビットフィールドを次のユニットに配置するか、隣接するユニットとオーバーラップするかは実装定義です。ユニット内のビットフィールドの割り当て順序 (上位から下位、または下位から上位) は実装定義です。アドレス可能なストレージ ユニットのアラインメントは指定されていません。

どのstruct型でも、型のアラインメントは少なくともその型のメンバーの最大アラインメントであり、型のサイズはそのアラインメントの倍数です。たとえば、構造体に (非ビット フィールド)intメンバーが含まれていて、 int4 バイトのアラインメントが必要な場合、構造体自体にも 4 バイト以上のアラインメントが必要です。

_Bool多くのコンパイラは、および型以外の整数型のビット フィールドを許可しintます。

少なくとも一部のコンパイラではstruct、ビット フィールドを含む のアラインメントは、少なくともビット フィールドの宣言された型のアラインメントです。たとえば、x86_64 上の gcc 4.7.2 の場合、次のようになります。

struct sb {
    _Bool bf:1;
};
struct si {
    unsigned bf:1;
};

gcc はstruct sb、1 バイトのサイズとアラインメント ( のサイズとアラインメント_Bool)、およびstruct si4 バイトのサイズとアラインメント ( のサイズとアラインメントint) を提供します。実装定義型のビット フィールドでも同じことを行います。として定義されたビット フィールドlong long bf:1;は、囲んでいる構造体のサイズと位置合わせを 8 バイトに強制します。どちらの場合も、ビット フィールドbfは幅が 1 ビットのオブジェクトですが、これは行われます。

SPARC/Solaris 9 上の Sun のコンパイラでも同様の動作が見られました。

実験では、 as_Boolまたは as のいずれかで定義された複数のビット フィールドunsignedを 1 バイト内の隣接するビットにパックできることが示されているため (実際にはこれが必要です)、ビット フィールド自体には厳密なアライメント要件はありません。

構造体メンバーのレイアウトは大部分が実装定義であることを理解しており、gcc の動作が C 標準に違反しているとは思いません。

だから私の質問(最後に!)は、なぜgcc(少なくとも1つの無関係なCコンパイラ、そしておそらくそれ以上)がこれを行うのですか?gcc の作成者は、宣言されたビット フィールドの型が含まれている構造体のサイズとアラインメントに影響を与える必要があると想定していますか? 彼らはこの仮定で正しいですか?私が見逃した C 標準自体の要件はありますか?

この動作を示すテスト プログラムを次に示します。システムで実行したい場合、一部の新しい機能をサポートしていない古いコンパイラを使用している場合、または特定の種類のビットを許可していないコンパイラを使用している場合は、その一部をコメントアウトする必要がある場合があります。田畑。gcc のように動作しないコンパイラがあるかどうか知りたいです。

#include <stdio.h>
#include <limits.h>
#include <stdint.h>
int main(void) {
    struct sb  { _Bool    bf:1; };
    struct s8  { uint8_t  bf:1; };
    struct s16 { uint16_t bf:1; };
    struct s32 { uint32_t bf:1; };
    struct s64 { uint64_t bf:1; };
    printf("sizeof (struct sb)  = %2zu (%2zu bits)\n",
           sizeof (struct sb),
           sizeof (struct sb)  * CHAR_BIT);
    printf("sizeof (struct s8)  = %2zu (%2zu bits)\n",
           sizeof (struct s8),
           sizeof (struct s8)  * CHAR_BIT);
    printf("sizeof (struct s16) = %2zu (%2zu bits)\n",
           sizeof (struct s16),
           sizeof (struct s16) * CHAR_BIT);
    printf("sizeof (struct s32) = %2zu (%2zu bits)\n",
           sizeof (struct s32),
           sizeof (struct s32) * CHAR_BIT);
    printf("sizeof (struct s64) = %2zu (%2zu bits)\n",
           sizeof (struct s64),
           sizeof (struct s64) * CHAR_BIT);
    return 0;
}

私のシステムで得られる出力は次のとおりです。

sizeof (struct sb)  =  1 ( 8 bits)
sizeof (struct s8)  =  1 ( 8 bits)
sizeof (struct s16) =  2 (16 bits)
sizeof (struct s32) =  4 (32 bits)
sizeof (struct s64) =  8 (64 bits)
4

1 に答える 1