4.2.4.8 構造体パッキング

CC-RHは,構造体メンバのアライメントをC言語レベルで指定できます。この機能は,-Xpackオプションと同等ですが,構造体パッキング指令はCソース内の任意の位置でアライメント値を指定できます。

注意

構造体をパッキングすると,データ領域を小さくできますが,プログラム・サイズは増え,実行速度も低下します。

(1)

構造体パッキングの形式

構造体パッキング機能は次の形式で指定します。

#pragma pack [(][1|2|4][)]

#pragma packは,この指令が出現した時点で,構造体メンバのアライメント値に変更します。この数値をパッキング値と呼び,指定できる数値は,1,2,4です。数値を指定しない場合,デフォルトのアライメントとなります。なお,この指令は出現した時点で有効となるのでCソース内に複数記述することができます。

#pragma pack 1  /*構造体メンバを1バイトのアライメントで整列*/
struct  TAG {
    char    c;
    int     i;
    short   s;
};

(2)

構造体パッキングのルール

構造体のメンバは,構造体のパッキング値とメンバの持つアライメント値の小さい方の値の整列条件を満たす形で並べられます。

たとえば,構造体のパッキング値が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バイト*/

 

(3)

共用体

共用体をパッキングの対象として構造体パッキングと同様に扱います。

例 1.

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 バイト*/

例 2.

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バイト*/

(4)

ビット・フィールド

ビット・フィールド要素の領域は次のように割り当てます

(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;

 

(5)

構造体オブジェクトの先頭の整列条件

構造体オブジェクトの先頭の整列条件は,構造パッキング機能を利用しなかったときと同じです。

(6)

構造体オブジェクトのサイズ

構造体のサイズが構造体の整列条件の値と構造体のパッキング値の小さい方の値の倍数になるようにパッキングを行います。オブジェクトの先頭の整列条件は構造パッキング機能を利用しなかったときと同じです。

例 1.

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バイト*/

 

例 2.

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バイト*/

 

(7)

構造体配列のサイズ

構造体オブジェクトの配列のサイズは要素である構造体オブジェクトのサイズに要素数を乗算した値です。

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バイト*/

 

(8)

オブジェクト間の領域

たとえば,次のソース・プログラムでは,sobj.c,sobj.i,cobjが隙間なく続いて配置される可能性があります(sobj,cobjの配置順は保証されません)。

#pragma pack 1
struct  S {
    char    c;
    int     i;
} sobj;
char    cobj;

 

(9)

構造体パッキング機能の注意点

(a)

-Xpackオプションと#pragma pack指令の同時指定について

Cソース中に#pragma pack指令で構造体パッキング指定がある時に-Xpackオプションを指定した場合,最初の#pragma pack指令が出現するまではオプション指定値がすべての構造体に適用されます。それ以降は#pragma pack指令の値が適用されます。

その後に#pragma pack(値なし)を書くと,#pragma pack(値なし)以降の行は,オプション指定値が適用されます。

(-Xpack=4を指定した場合)

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 バイト*/

(c)

#pragma packの入れ子指定

異なる#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;     /* 正しくアクセスできない場合があります */
}