CC-RHは,構造体メンバのアライメントをC言語レベルで指定できます。この機能は,-Xpackオプションと同等ですが,構造体パッキング指令はCソース内の任意の位置でアライメント値を指定できます。
注意 | 構造体をパッキングすると,データ領域を小さくできますが,プログラム・サイズは増え,実行速度も低下します。 |
構造体パッキング機能は次の形式で指定します。
#pragma pack [(][1|2|4][)]
|
#pragma packは,この指令が出現した時点で,構造体メンバのアライメント値に変更します。この数値をパッキング値と呼び,指定できる数値は,1,2,4です。数値を指定しない場合,デフォルトのアライメントとなります。なお,この指令は出現した時点で有効となるのでCソース内に複数記述することができます。
#pragma pack 1 /*構造体メンバを1バイトのアライメントで整列*/
struct TAG {
char c;
int i;
short s;
};
|
構造体のメンバは,構造体のパッキング値とメンバの持つアライメント値の小さい方の値の整列条件を満たす形で並べられます。
たとえば,構造体のパッキング値が2のときメンバの形がint型ならば2バイトの整列条件を満たす形で並べられます。
struct S {
char c; /*1バイトの整列条件を満たす*/
int i; /*4バイトの整列条件を満たす*/
};
#pragma pack 1
struct S1 {
char c; /*1バイトの整列条件を満たす*/
int i; /*1バイトの整列条件を満たす*/
};
#pragma pack 2
struct S2 {
char c; /*1バイトの整列条件を満たす*/
int i; /*2バイトの整列条件を満たす*/
};
struct S sobj; /*サイズ8バイト*/
struct S1 s1obj; /*サイズ5バイト*/
struct S2 s2obj; /*サイズ6バイト*/
|
共用体をパッキングの対象として構造体パッキングと同様に扱います。
union U {
char c;
int i;
};
#pragma pack 1
union U1 {
char c;
int i;
};
#pragma pack 2
union U2 {
char c;
int i;
};
union U uobj; /*サイズ4 バイト*/
union U1 u1obj; /*サイズ4 バイト*/
union U2 u2obj; /*サイズ4 バイト*/
|
union U {
int i:7;
};
#pragma pack 1
union U1 {
int i:7;
};
#pragma pack 2
union U2 {
int i:7;
};
union U uobj; /*サイズ4バイト*/
union U1 u1obj; /*サイズ1バイト*/
union U2 u2obj; /*サイズ2バイト*/
|
ビット・フィールド要素の領域は次のように割り当てます
(a) | 構造体のパッキング値がメンバの型の整列条件値と等しいあるいは大きい場合 |
構造体パッキング機能を利用しなかったときと同じように割り当てます。つまり,続けて割り当てるとその領域がメンバの型の整列条件を満たす境界を越えてしまう場合,その整列条件を満たしている領域から割り当てます。
(b) | 構造体のパッキング値が要素の型の整列条件値より小さい場合 |
- | 続けて割り当てるとその領域を含むバイト数が要素の型よりも大きくなる場合
構造体のパッキング値の整列条件を満たす形で割り当てます。 |
struct S {
short a:7; /*0〜6ビット目*/
short b:7; /*7〜13ビット目*/
short c:7; /*16〜22ビット目(2バイト境界に整列)*/
short d:15; /*32〜46ビット目(2バイト境界に整列)*/
} sobj;
#pragma pack 1
struct S1 {
short a:7; /*0〜6ビット目*/
short b:7; /*7〜13ビット目*/
short c:7; /*14〜20ビット目*/
short d:15; /*24〜38ビット目(バイト境界に整列)*/
} s1obj;
|
構造体オブジェクトの先頭の整列条件は,構造パッキング機能を利用しなかったときと同じです。
構造体のサイズが構造体の整列条件の値と構造体のパッキング値の小さい方の値の倍数になるようにパッキングを行います。オブジェクトの先頭の整列条件は構造パッキング機能を利用しなかったときと同じです。
struct S {
int i;
char c;
};
#pragma pack 1
struct S1 {
int i;
char c;
};
#pragma pack 2
struct S2 {
int i;
char c;
};
struct S sobj; /*サイズ8バイト*/
struct S1 s1obj; /*サイズ5バイト*/
struct S2 s2obj; /*サイズ6バイト*/
|
struct S {
int i;
char c;
};
struct T {
char c;
struct S s;
};
#pragma pack 1
struct S1 {
int i;
char c;
};
struct T1 {
char c;
struct S1 s1;
};
#pragma pack 2
struct S2 {
int i;
char c;
};
struct T2 {
char c;
struct S2 s2;
};
struct T tobj; /*サイズ12バイト*/
struct T1 t1obj; /*サイズ 6バイト*/
struct T2 t2obj; /*サイズ 8バイト*/
|
構造体オブジェクトの配列のサイズは要素である構造体オブジェクトのサイズに要素数を乗算した値です。
struct S {
int i;
char c;
};
#pragma pack 1
struct S1 {
int i;
char c;
};
#pragma pack 2
struct S2 {
int i;
char c;
};
struct S sobj[2]; /*サイズ16バイト*/
struct S1 s1obj[2]; /*サイズ10バイト*/
struct S2 s2obj[2]; /*サイズ12バイト*/
|
たとえば,次のソース・プログラムでは,sobj.c,sobj.i,cobjが隙間なく続いて配置される可能性があります(sobj,cobjの配置順は保証されません)。
#pragma pack 1
struct S {
char c;
int i;
} sobj;
char cobj;
|
(a) | -Xpackオプションと#pragma pack指令の同時指定について |
Cソース中に#pragma pack指令で構造体パッキング指定がある時に-Xpackオプションを指定した場合,最初の#pragma pack指令が出現するまではオプション指定値がすべての構造体に適用されます。それ以降は#pragma pack指令の値が適用されます。
その後に#pragma pack(値なし)を書くと,#pragma pack(値なし)以降の行は,オプション指定値が適用されます。
struct S2 {...}; /*オプションでパッキング値4を指定している
-Xpack=4オプションが有効:パッキング値4*/
#pragma pack 2 /*#pragma指令でパッキング2を指定している
struct S1 {...}; pragma pack(2)が有効:パッキング値2*/
#pragma pack /*#pragma指令でパッキング値に指定なし
struct S2_2 {...}; -Xpack=4オプションが有効:パッキング値4*/
|
(b) | 構造体のパッキング値とメンバの持つアライメント値 |
構造体のメンバは,構造体のパッキング値とメンバの持つアライメント値の小さい方の値の整列条件を満たす形で並べられます。たとえば,構造体のパッキング値が2 のときメンバの形がlong 型ならば2 バイトの整列条件を満たす形で並べられます。
struct S {
char c; /*1バイトの整列条件を満たす*/
long i; /*4バイトの整列条件を満たす*/
};
#pragma pack(1)
struct S1 {
char c; /*1バイトの整列条件を満たす*/
long i; /*1バイトの整列条件を満たす*/
};
#pragma pack(2)
struct S2 {
char c; /*1バイトの整列条件を満たす*/
long i; /*2バイトの整列条件を満たす*/
};
struct S sobj; /*サイズ8 バイト*/
struct S1 s1obj; /*サイズ5 バイト*/
struct S2 s2obj; /*サイズ6 バイト*/
|
異なる#pragma pack 指定された構造体オブジェクトの入れ子は,次のように定義します。
アライメントが異なるメンバを持つ構造体・共用体メンバに対しては,警告を出します。
警告対象のメンバは,ソース・コードに書かれたとおりの#pragma pack に従います。
#pragma pack 1
struct ST1
{
char c;
#pragma pack 4
struct ST4 //size=8, align=4 (型としては4)
{
char c; //offset=1
short s; //offset=3
int i; //offset=5
} st4; //size=8, align=1 (ST1のメンバなので1)
//メンバst4の位置で警告
int i;
} st1; //size=13, align=1
|
[注意事項]
pack=1 もしくは#pragma pack 1 を指定した構造体,共用体,クラスのメンバはポインタを用いてアクセスすることはできません(ポインタを使用したメンバ関数内でのアクセスを含みます)。
例
#pragma pack 1
struct st {
char x;
int y;
} ST;
int *p = &ST.y; /* ST.y のアドレスが奇数になる場合があります */
void func(void){
ST.y =1; /* 正しくアクセスできます */
*p = 1; /* 正しくアクセスできない場合があります */
}
|