15.10 構造体、共用体、列挙型、ビットフィールド

ISO C 標準での必要に応じて、構造体、共用体、列挙型、およびビットフィールドに関する ARM C コンパイラおよび C ライブラリの実装定義機能について説明します。

ISO/IEC C 標準では、構造化されたデータの型について、以下の実装の詳細を文書化することが要求されています。
  • 共用体のメンバが、異なる型のメンバを使用してアクセスされた場合の結果。
  • 構造体メンバのパディングと境界整列。
  • プレーンな int ビットフィールドが signed int ビットフィールドと unsigned int ビットフィールドのどちらとして処理されるのか。
  • ユニット内のビットフィールドの割り当て順序。
  • ビットフィールドが記憶域ユニットの境界を超えることが許されるかどうか。
  • 列挙型の値を表現するときに使用される整数型。

共用体

異なる型のメンバを使用して union のメンバにアクセスするときは、元の型の表現から結果の値を予測できます。エラーは発生しません。

Enumerations

enum 型のオブジェクトは、その enum の範囲を含む最小の整数型で実装されます。
C モード、および --enum_is_int を使用していない C++ モードでは、 enum に正の列挙値のみが含まれている場合、 enum のストレージ型は、以下に示した型のうち、 enum 内の列挙子の範囲に基づく最初の unsigned 型になります。その他のモードでは、 enum に負の列挙値が 1 つでも含まれている場合、 enum のストレージ型は、以下に示した型のうち、 enum 内の列挙子の範囲に基づく最初の型になります。
  • --enum_is_int を使用していない場合は unsigned char
  • --enum_is_int を使用していない場合は signed char
  • --enum_is_int を使用していない場合は unsigned short
  • --enum_is_int を使用していない場合は signed short
  • signed int
  • C で --strict を指定している場合を除き、 unsigned int
  • C で --strict を指定している場合を除き、 signed long long
  • C で --strict を指定している場合を除き、 unsigned long long

  • RVCT 4.0 では、 enum のストレージ型が、リストの最初の unsigned 型になるという説明は、GNU(--gnu)モードにのみ当てはまります。
  • ARM® コンパイラ 4.1 以降では、 enum のストレージ型が、リストの最初の unsigned 型になるという説明は、モードに関係なく当てはまります。
このように enum を実装することで、データサイズを小さくできます。コマンドラインオプション --enum_is_int を使用して、 enum の基底型を最低でも int の幅に設定できます。
詳細については、『Procedure Call Standard for the ARM® Architecture』仕様の C 言語マッピングに関する説明を参照して下さい。

--enum_is_int オプションを使用または使用せずにコンパイルされており、インタフェースまたはデータ構造を共有している変換単位を混合するときは、注意が必要です。
厳密な C では、列挙値は -2147483648 ~ +2147483647(境界値を含む)の範囲の int で表現できる必要があります。列挙値が範囲外の場合、以下の警告が表示されます。
#66:列挙値が "int" の範囲外です
このような値は C++ でも同様に、 unsigned int long long 、または unsigned long long として扱われます。
列挙値が範囲外であることを示す警告が表示されるようにするには、以下のコマンドを使用して警告をエラーに変更します。
armcc --diag_error=66 ...

構造体のパディングと境界整列

ここで説明する内容は以下に適用されます。
  • すべての C 構造体
  • 仮想関数またはベースクラスを使用しないすべての C++ 構造体とクラス
フィールドが正しく整列され、かつ構造自体が正しく整列するように、構造体にはパディングを挿入できます。以下の図は、従来のパックされていない構造体の例を示します。フィールドを正しく整列するために、バイト 1、2、3 にパディングを挿入しています。また、構造体自体を正しく整列するために、バイト 11、12 にパディングを挿入しています。sizeof() 関数では、このパディングを含めた構造体のサイズが返されます。
図 15-1 従来のパックされていない構造体の例
この図を表示するには、ご使用のブラウザが SVG 形式をサポートしている必要があります。ネイティブでサポートしているブラウザをインストールするか、次のような適切なプラグインをインストールします。Adobe SVG Viewer。.

コンパイラでは、構造体の定義方法に応じて、以下のいずれかの方法で構造体にパディングを挿入します。
  • static または extern として定義されている構造は、ゼロでパディングされます。
  • malloc() または auto でスタックまたはヒープ上に定義される構造体には、そのメモリ位置に以前に格納されていた値をそのまま埋め込みます。memcmp() を使用して、このように定義されたパディング済みの構造体を比較することはできません。
--remarks オプションを使用すると、コンパイラが struct にパディングを挿入したときに生成されるメッセージを表示できます。
C++ では、以下のように空のイニシャライザを備えた構造体が許可されます。
struct
{
    int x;
} X = { };
ただし、C をコンパイルしているとき、または --cpp および --c90 オプションを指定して C++ をコンパイルしているときは、エラーが生成されます。

Bitfields

パックされていない構造体では、 ARM コンパイラによってコンテナ内にビットフィールドが割り当てられます。コンテナは、型が宣言され、正しく整列されたオブジェクトです。
ビットフィールドは、コンフィギュレーションに基づいて、指定された最初のフィールドがワード内の最下位アドレスのビットを占有するように割り当てられます。
リトルエンディアン
最下位アドレスが最下位の内容になります。
ビッグエンディアン
最下位アドレスが最上位の内容になります。
ビットフィールドコンテナには、任意の整数型を指定できます。

厳密な 1990 ISO 標準 C では、ビットフィールドに指定できる型は int signed int 、および unsigned int のみです。 int 型以外をビットフィールドに指定すると、コンパイラからエラーが表示されます。
signed 修飾子も unsigned 修飾子も付けずに宣言されたプレーンなビットフィールドは、 unsigned として扱われます。例えば、int x:10 では、10 ビットの符号なし整数が割り当てられます。
ビットフィールドは、以下のように未割り当てのビットが十分にある、適切な型の最初のコンテナに割り当てられます。
struct X
{
    int x:10;
    int y:20;
};
最初の宣言では、整数コンテナを作成し、x に 10 ビットを割り当てます。2 番目の宣言では、コンパイラによって未割り当てビットが十分にある既存の整数コンテナが検索され、x と同じコンテナ内に y を割り当てます。
1 つのビットフィールドは、コンテナ内に完全に含まれます。コンテナ内に収まらないビットフィールドは、次の同じ型のコンテナ内に配置されます。例えば、以下のように上記の構造体にビットフィールド z を追加で宣言すると、コンテナがオーバーフローします。
struct X
{
    int x:10;
    int y:20;
    int z:5;
};
コンパイラでは、最初のコンテナの残り 2 ビットにパディングを挿入し、新しい整数コンテナを z に割り当てます。
ビットフィールドコンテナは、以下のように互いに重複させることができます。
struct X
{
    int x:10;
    char y:2;
};
最初の宣言では、整数コンテナを作成し、x に 10 ビットを割り当てます。これらの 10 ビットは、整数コンテナの先頭バイトと 2 バイト目の 2 ビットを占有します。2 番目の宣言では、コンパイラによって char 型のコンテナがチェックされます。適切なコンテナがないため、コンパイラは正しく整列された char コンテナを新たに割り当てます。
char の自然配列は 1 であるため、コンパイラはそのビットフィールドを完全に収容することができる未割り当てビットを含む最初のバイトを検索します。上記の例では、 int コンテナの 2 番目のバイトのうち、2 ビットが x に割り当てられ、6 ビットが未割り当てです。コンパイラは、 char コンテナの割り当てを前の int コンテナの 2 バイト目から始め、x に割り当てられている最初の 2 ビットをスキップして、2 ビットを y に割り当てます。
y が宣言された char y:8 である場合、コンパイラは 2 バイト目をパッドし、新しい char コンテナを 3 バイト目に割り当てます。これは、ビットフィールドがそのコンテナをオーバーフローすることはできないためです。以下の図は、以下のサンプル構造体のビットフィールドの割り当てを示しています。
struct X
{
    int x:10;
    char y:8;
};
図 15-2 ビットフィールドの割り当て 1
この図を表示するには、ご使用のブラウザが SVG 形式をサポートしている必要があります。ネイティブでサポートしているブラウザをインストールするか、次のような適切なプラグインをインストールします。Adobe SVG Viewer。.

異なる型のコンテナのビットフィールド宣言にも同じ基本規則が適用されます。例えば、上記の例に int ビットフィールドを追加すると以下のようになります。
struct X
{
    int x:10;
    char y:8;
    int z:5;
}
コンパイラは、 int コンテナの割り当てを int x:10 コンテナと同じ位置から始め、バイト境界で整列された char と 5 ビットのビットフィールドを以下のように割り当てます。
図 15-3 ビットフィールドの割り当て 2
この図を表示するには、ご使用のブラウザが SVG 形式をサポートしている必要があります。ネイティブでサポートしているブラウザをインストールするか、次のような適切なプラグインをインストールします。Adobe SVG Viewer。.

名前を付けずにサイズが 0 のビットフィールドを宣言することで、ビットフィールドコンテナに明示的にパディングを挿入できます。コンテナが空でなければ、サイズが 0 のビットフィールドでコンテナが完全に埋められます。その後のビットフィールド宣言は、新しい空のコンテナから始まります。

最適化として、ビットフィールドが書き込まれるときに、コンパイラがコンテナ内のパディングビットを指定されていない値で上書きする場合もあります。この動作は、ビットフィールドの通常の使用には影響を及ぼしません。
非機密扱いPDF file icon PDF 版ARM DUI0472LJ
Copyright © 2010-2015 ARM.All rights reserved.