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

このセクションでは、構造化されたデータ型を実装する union、enum、および struct について説明します。また、構造体のパディングとビットフィールドの実装についても説明します。

詳細については、匿名クラス、匿名構造体、匿名共用体を参照して下さい。

Show/hide共用体

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

Show/hide列挙型

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

Note

  • 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 言語マッピングに関する説明を参照して下さい。

Note

--enum_is_int オプションを使用または使用せずにコンパイルされており、インタフェースまたはデータ構造を共有している変換ユニットを混合するときは、注意が必要です。

範囲外の値の処理

厳密な C では、列挙値は例えば -2147483648 ~ +2147483647 の範囲の int で表現できる必要があります。以前のリリースの RVCT では、--strict オプションを指定した場合を除き、範囲外の値は警告が表示されることなく int にキャストされました。

RVCT v2.2 以上では、列挙値が範囲外の場合、以下の警告が表示されます。

#66: 列挙値が "int" の範囲外です

このような値は C++ でも同様に、unsigned intlong long、または unsigned long long として扱われます。

列挙値が範囲外であることを示す警告が表示されるようにするには、以下のコマンドを使用して警告をエラーに変更します。

armcc --diag_error=66 ...

Show/hide構造体

ここで説明する内容は以下に適用されます。

  • すべての C 構造体

  • 仮想関数またはベースクラスを使用しないすべての C++ 構造体とクラス

構造体の境界整列

パックされていない構造体の境界整列は、その構造体のフィールドに必要な最大の境界整列になります。

フィールドの境界整列

構造体は、名前が付けられた最初のコンポーネントが最下位アドレスになるように配置されます。フィールドは、以下のように整列されます。

  • char 型のフィールドは、次に使用可能なバイトに揃えて整列されます。

  • short 型のフィールドは、次の偶数アドレスのバイトに揃えて整列されます。

  • RVCT 2.0 以降では、double 型と long long 型が 8 バイト境界で整列されます。このため、ARMv5TE 以降では LDRD 命令と STRD 命令を効率的に使用できます。

  • ビットフィールドの境界整列は、そのビットフィールドの宣言方法によって異なります。詳細については、パック構造体のビットフィールドを参照して下さい。

  • その他のすべての型は、ワード境界で整列されます。

フィールドが正しく整列され、かつ構造自体が正しく整列するように、構造体にはパディングを挿入できます。Figure 3は、従来の非パック構造体の例を示しています。フィールドを正しく整列するために、バイト 1、2、3 にパディングを挿入しています。また、構造体自体を正しく整列するために、バイト 11、12 にパディングを挿入しています。sizeof() 関数では、このパディングを含めた構造体のサイズが返されます。

Figure 3. 従来のパックされていない構造体の例

To view this graphic, your browser must support the SVG format. Either install a browser with native support, or install an appropriate plugin such as Adobe SVG Viewer.


コンパイラでは、構造体の定義方法に応じて、以下のいずれかの方法で構造体にパディングを挿入します。

  • static または extern として定義されている構造は、ゼロでパディングされます。

  • malloc() または auto でスタックまたはヒープ上に定義される構造体には、そのメモリ位置に以前に格納されていた値をそのまま埋め込みます。memcmp() を使用して、このように定義されたパディング済みの構造体を比較することはできません(Figure 3 を参照)。

--remarks オプションを使用すると、コンパイラが struct にパディングを挿入したときに生成されるメッセージを表示できます。

C++ では、以下のように空のイニシャライザを備えた構造体が許可されます。

struct
{
    int x;
} X = { };

ただし、C をコンパイルしているとき、または --cpp および --c90 オプションを指定して C++ をコンパイルしているときは、エラーが生成されます。

Show/hideパック構造体

パック構造体とは、構造体自体の境界整列と、構造体内のフィールドの境界整列が常に 1 の構造体です。

パック構造体は、__packed 修飾子で定義できます。または、非境界整列データを持つ構造体が確実にパックされるように #pragma pack(n) を使用することもできます。構造体のデフォルトのパック方法を変更するコマンドラインオプションはありません。

Show/hideビットフィールド

パックされていない構造体では、ARM コンパイラによってコンテナ内にビットフィールドが割り当てられます。コンテナは、型が宣言され、正しく整列されたオブジェクトです。

ビットフィールドは、コンフィギュレーションに基づいて、指定された最初のフィールドがワード内の最下位アドレスのビットを占有するように割り当てられます。

リトルエンディアン

最下位アドレスが最下位の内容になります。

ビッグエンディアン

最下位アドレスが最上位の内容になります。

ビットフィールドコンテナには、任意の整数型を指定できます。

Note

厳密な 1990 ISO 標準 C では、ビットフィールドに指定できる型は intsigned 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 に割り当てます。

ychar y:8 と宣言されている場合、コンテナをオーバーフローさせることはできないため、コンパイラは 2 バイト目の残りのビットにパディングを挿入し、char コンテナを新しく 3 バイト目に割り当てます。以下の例のような構造体のビットフィールドの割り当てをFigure 4に示します。

struct X
{
    int x:10;
    char y:8;
};

Figure 4. ビットフィールドの割り当て 1

To view this graphic, your browser must support the SVG format. Either install a browser with native support, or install an appropriate plugin such as Adobe SVG Viewer.


Note

異なる型のコンテナのビットフィールド宣言にも同じ基本規則が適用されます。例えば、上記の例に int ビットフィールドを追加すると以下のようになります。

struct X
{
    int x:10;
    char y:8;
    int z:5;
}

コンパイラは、int コンテナの割り当てを int x:10 コンテナと同じ位置から始め、バイト境界で整列された char と 5 ビットのビットフィールドを割り当てます(Figure 5 を参照)。

Figure 5. ビットフィールドの割り当て 2

To view this graphic, your browser must support the SVG format. Either install a browser with native support, or install an appropriate plugin such as Adobe SVG Viewer.


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

Show/hideパック構造体のビットフィールド

パック構造体内のビットフィールドコンテナの境界整列は 1 です。したがって、パック構造体内のビットフィールドにパディングされる最大ビット数は 7 ビットです。パックされない構造体の場合、挿入されるパディングは最大 8*sizeof(container-type)-1 ビットです。

Copyright © 2010 ARM. All rights reserved.ARM DUI 0491BJ
Non-ConfidentialID011811