構造体のメモリ領域の割り当てとバイトの位置合わせ
メモリアライメントについて1.メモリアライメントとは2つの変数を同時に宣言すると仮定します:char a;short b; 変数a,bのアドレスを&(アドレス記号を取る)で観察すると(16ビットCPUを例にとると):aのアドレスが0 x 0000であれば、bのアドレスは0 x 002または0 x 004になる.では、0 x 001というアドレスが使用されていないのに、何をしているのでしょうか.答えは確かに使われていないことです.CPUは、毎回、2バイト(16ビットCPU)または4バイト(32ビットCPU)の整数倍のメモリアドレスからデータを読み込むからである.変数bのアドレスが0 x 001であれば、CPUはまず0 x 0000から1ショットを読み出し、その上位8ビットをbの下位8ビットに入れ、その後0 x 002から次のショットを読み出し、その下位8ビットをbの上位8ビットに入れる必要があり、これではbの値を得るために2回の読み出し操作が必要となる.しかし、bのアドレスが0 x 002であれば、CPUは1回の読み出し操作でbの値を得ることができる.したがって、コンパイラはコードを最適化するために、変数の大きさに応じて適切な位置に指定することが多い.すなわち、メモリアライメントと呼ばれる(変数bをメモリアライメントし、a、b間のメモリが浪費され、aはメモリを多く占めていない).2.構造体メモリ整列規則構造体が占有するメモリは、そのメンバーの構造体における宣言順序に関係し、そのメンバーのメモリ整列規則は以下の通りである:(1)各メンバーはそれぞれ自分の整列バイト数とPPB(指定された整列バイト数、32ビットマシンのデフォルトは4)2バイト数の最小の整列であり、長さを最小化することができる.(2)構造などの複雑なタイプのデフォルトの位置合わせは、最も長いメンバーの位置合わせであり、メンバーが複雑なタイプの場合、長さを最小限に抑えることができます.(3)構造体の整列後の長さは、メンバーの最大整列パラメータ(PPB)の整数倍でなければならず、配列を処理する際に各項目が境界整列することを保証することができる.(4)構造体のメモリが大きい場合は、各メンバーのオフセットアドレスをリストすべきであり、その長さ=最後のメンバーのオフセットアドレス+最後のメンバー数の長さ+最後のメンバーの調整パラメータ(PPBを考慮)である.
次に、上記のルールを例に挙げて説明します.
最終出力の結果は:8です.文#pragma pack(2)の役割は、構造体を2バイトで整列させること、すなわちPPB=2を指定することである.次のように分析されます.
変数aはデフォルトで1バイト、PB=2であるため、aは1バイトで整列し、aのオフセットアドレスは0である.
変数bのデフォルトは4バイト(32ビットマシンでintは4バイト)であり、PB=2であるため、bは2バイトで整列し、bのオフセットアドレスは2である.
変数cはデフォルトで1バイト、PB=2であるため、cは1バイトで整列し、オフセットアドレスは6である.
このとき構造体の算出バイト数は7バイトである.最後にルール3に従って,構造体が整列したバイト数は8である.sizeof(T)=6+1+1=8
3.例
(1)#pragma pack(2)//PPBを2に指定
sizeof(T)=最後のメンバのオフセットアドレス+最後のメンバ数の長さ=2+4=6となる.
PPB=4、sizeof(T 1)=4+4=8;sizeof(T 2)=8+1=9,9は4を除くことができないので、整数を3に調整します.すなわちsizeof(T 2)=8+1+3=12です.
4.注意点
(1)バイトの位置合わせはコンパイラに依存する.
(2)PPBサイズに注意し、PPBサイズはpragam pack(n)で指定する.
(3)構造体が占有するバイト数はPPBで割り切れる.
2:
(1)sizeofは、1つの関数に対して評価を呼び出すこともでき、その結果、関数はタイプのサイズを返し、関数は呼び出されません.
(2)struct構造体のメモリ割り当ての問題がやっと分かった.構造体の各メンバーのバイト位置合わせは以下のいくつかの原則に従う:直接以下のいくつかの原則で構造体の大きさを判断することができる
1.構造体の各メンバーの構造体ヘッダアドレスに対するオフセット量(offset)は、必要に応じてコンパイラがメンバー間にパディングバイトを追加するメンバーサイズの整数倍である.
たとえば、次の構造体があります.
1番目のメンバオフセット量は0であり、int型メンバサイズ4(これがあまりにも機械的な整数長が4バイトを占めると仮定)の整数倍である.
2番目のメンバtはchar型であり、彼のサイズは1であり、まずメンバiとtの間にバイトが充填されていないと仮定する.iは整数型であり、4バイトを占めるため、充填されていない前に、2番目のメンバtの構造体に対するオフセット量は4であり、彼はtメンバのサイズ1の4倍であり、このエントリに合致するため、システムは構造体の2番目のメンバにメモリを割り当てるとき、iとtの間にバイトは埋め込まれず、整列の目的に達します.
構造体の3番目のメンバーnを割り当てると、まず、サイズが4であり、充填されていない前に、nの構造体ヘッダアドレスに対するオフセット量は、前の2つのメンバー+充填バイト=5であることが判明するので、システムが5が4(メンバーサイズ)の整数倍ではないことを発見すると、メンバーtの後(またはnの前)に3バイトが充填され、nのオフセット量を8にして4の整数倍にする.このように、この構造体がメモリを占有する場合は、一時的に4+1+3+4となる.
2.構造体の合計サイズは、構造体の最も広い基本タイプのメンバーサイズの整数倍であり、必要に応じてコンパイラは、最後のメンバーの後にパディングバイト(trailingpadding)を加算します.
上記の構造体メモリの割り当ては、最後のメンバーの後にバイト数を入力する必要がないと仮定すると、この構造体のサイズは12になります.一方、ex構造体では最も広い基本タイプのメンバーがintで、サイズが4、12が4の整数倍なので、最後のメンバーの後にパディングバイトを加える必要はありません.だからsizeof(ex)=12;
構造体が次のように表示されている場合:
ではsizeof(ex 1)=16;なぜなら、最後のメンバーの後に3バイトが埋め込まれているからです.
3.さらに、構造体変数のヘッダアドレスは、その最も広い基本タイプのメンバーのサイズによって除去することができる.
4.構造体メンバー属性に構造体変数が含まれている複合構造体について、最も広い基本タイプのメンバーを再決定する場合は、複合タイプのメンバーのサブメンバーを含める必要があります.ただし、複合タイプメンバーのオフセット位置を決定する場合は、複合タイプを全体として見ます.
5構造体のサイズは、最後のメンバーのオフセット量に加えて、最後のパディングバイト数を加えたものと同じです.
sizeof( struct ) = offsetof( last item ) + sizeof( last item ) +sizeof( trailing padding )
構造体バイトの位置合わせに関する良いブログをお勧めします.http://www.cnblogs.com/motadou/archive/2009/01/17/1558438.html
次に、上記のルールを例に挙げて説明します.
#include
#pragma pack(2) // PPB 2
struct T
{
char a; // 0
int b; // 2
char c; // 6
};
#pragma pack() // PPB,32 4
int main(int argc,char * argv[])
{
printf("sizeof(struct T));
return 0;
}
最終出力の結果は:8です.文#pragma pack(2)の役割は、構造体を2バイトで整列させること、すなわちPPB=2を指定することである.次のように分析されます.
変数aはデフォルトで1バイト、PB=2であるため、aは1バイトで整列し、aのオフセットアドレスは0である.
変数bのデフォルトは4バイト(32ビットマシンでintは4バイト)であり、PB=2であるため、bは2バイトで整列し、bのオフセットアドレスは2である.
変数cはデフォルトで1バイト、PB=2であるため、cは1バイトで整列し、オフセットアドレスは6である.
このとき構造体の算出バイト数は7バイトである.最後にルール3に従って,構造体が整列したバイト数は8である.sizeof(T)=6+1+1=8
3.例
(1)#pragma pack(2)//PPBを2に指定
struct T
{
char a; // 0
char b; // 1
int c; // 2
};
sizeof(T)=最後のメンバのオフセットアドレス+最後のメンバ数の長さ=2+4=6となる.
struct T1
{
char a; // 0
char b; // 1
int c; // 4
};
struct T2
{
char a; // 0
int b; // 4
char c; // 8
};
PPB=4、sizeof(T 1)=4+4=8;sizeof(T 2)=8+1=9,9は4を除くことができないので、整数を3に調整します.すなわちsizeof(T 2)=8+1+3=12です.
4.注意点
(1)バイトの位置合わせはコンパイラに依存する.
(2)PPBサイズに注意し、PPBサイズはpragam pack(n)で指定する.
(3)構造体が占有するバイト数はPPBで割り切れる.
2:
(1)sizeofは、1つの関数に対して評価を呼び出すこともでき、その結果、関数はタイプのサイズを返し、関数は呼び出されません.
(2)struct構造体のメモリ割り当ての問題がやっと分かった.構造体の各メンバーのバイト位置合わせは以下のいくつかの原則に従う:直接以下のいくつかの原則で構造体の大きさを判断することができる
1.構造体の各メンバーの構造体ヘッダアドレスに対するオフセット量(offset)は、必要に応じてコンパイラがメンバー間にパディングバイトを追加するメンバーサイズの整数倍である.
たとえば、次の構造体があります.
structex
{
int i;
char t;
int n;
};
1番目のメンバオフセット量は0であり、int型メンバサイズ4(これがあまりにも機械的な整数長が4バイトを占めると仮定)の整数倍である.
2番目のメンバtはchar型であり、彼のサイズは1であり、まずメンバiとtの間にバイトが充填されていないと仮定する.iは整数型であり、4バイトを占めるため、充填されていない前に、2番目のメンバtの構造体に対するオフセット量は4であり、彼はtメンバのサイズ1の4倍であり、このエントリに合致するため、システムは構造体の2番目のメンバにメモリを割り当てるとき、iとtの間にバイトは埋め込まれず、整列の目的に達します.
構造体の3番目のメンバーnを割り当てると、まず、サイズが4であり、充填されていない前に、nの構造体ヘッダアドレスに対するオフセット量は、前の2つのメンバー+充填バイト=5であることが判明するので、システムが5が4(メンバーサイズ)の整数倍ではないことを発見すると、メンバーtの後(またはnの前)に3バイトが充填され、nのオフセット量を8にして4の整数倍にする.このように、この構造体がメモリを占有する場合は、一時的に4+1+3+4となる.
2.構造体の合計サイズは、構造体の最も広い基本タイプのメンバーサイズの整数倍であり、必要に応じてコンパイラは、最後のメンバーの後にパディングバイト(trailingpadding)を加算します.
上記の構造体メモリの割り当ては、最後のメンバーの後にバイト数を入力する必要がないと仮定すると、この構造体のサイズは12になります.一方、ex構造体では最も広い基本タイプのメンバーがintで、サイズが4、12が4の整数倍なので、最後のメンバーの後にパディングバイトを加える必要はありません.だからsizeof(ex)=12;
構造体が次のように表示されている場合:
struc test1
{
int i;
char t;
int n;
char add;
};
ではsizeof(ex 1)=16;なぜなら、最後のメンバーの後に3バイトが埋め込まれているからです.
3.さらに、構造体変数のヘッダアドレスは、その最も広い基本タイプのメンバーのサイズによって除去することができる.
4.構造体メンバー属性に構造体変数が含まれている複合構造体について、最も広い基本タイプのメンバーを再決定する場合は、複合タイプのメンバーのサブメンバーを含める必要があります.ただし、複合タイプメンバーのオフセット位置を決定する場合は、複合タイプを全体として見ます.
5構造体のサイズは、最後のメンバーのオフセット量に加えて、最後のパディングバイト数を加えたものと同じです.
sizeof( struct ) = offsetof( last item ) + sizeof( last item ) +sizeof( trailing padding )
構造体バイトの位置合わせに関する良いブログをお勧めします.http://www.cnblogs.com/motadou/archive/2009/01/17/1558438.html