#pragma twice

KAB-studio > プログラミング > #pragma twice > 234 Version 12.11 パディングとアライメント

#pragma twice 234 Version 12.11 パディングとアライメント

前のページへ 表紙・目次へ 次のページへ

 Version 12.11
パディングとアライメント

前回は複数データの書き込みと読み込みをしてみました
 fread() と fwrite() 使ってね
でも、データひとつひとつを書き込むっていうのはちょっと面倒、という
わけで今回は構造体を使ってみます
あ、構造体丸ごと書き込んだりすれば簡単だもんね
そういうこと。でもそこで注意しなきゃいけない点があるから
注意点?
まずはファイルは抜かして考えようか。 Version 12.01 ( No.224 ) を思
い出して
ん……あ、そういえば構造体のサイズってやったね
そう。たとえば

struct TEST_STRUCT
{
    char m_ch;
    int m_i;
};

って構造体があるとします。このサイズは?
 1 + 4 = 5 ……じゃないんだよね
そう、答えは 8 

void SizeOf_TEST_STRUCT()
{
    TRACE( "%d\n", sizeof( TEST_STRUCT ) );
    // 8
}

謎よねー
ではもうひとつ

struct TEST_STRUCT_DOUBLE
{
    char m_ch;
    double m_d;
};

の構造体
 int じゃなくて double になってる
このサイズは、というと

void SizeOf_TEST_STRUCT_DOUBLE()
{
    TRACE( "%d\n", sizeof( TEST_STRUCT_DOUBLE ) );
    // 16
}

じゅ、 16 !? なんでそんなに大きいの!? だって、 double って 8 
バイト……あ!
はい火美ちゃん
もしかして、 double の倍のサイズになってる!? さっきのは int の
倍だし
その通り! と、もうひとつ例を見ておこうか

struct TEST_STRUCT_DOUBLE_2
{
    char m_ch;
    double m_d;
    char m_ch2;
};

後ろに char が増えた
これのサイズはというと

void SizeOf_TEST_STRUCT_DOUBLE_2()
{
    TRACE( "%d\n", sizeof( TEST_STRUCT_DOUBLE_2 ) );
    // 24
}

あ”、 24 、ってことは double の3倍?
そういうこと
……つまり、変数が3つあるから、3倍?
ほとんどそうだけど、ちょっと違うかな。正確に言うと、それぞれの変数
に【無駄な部分】が加えられて double と同じサイズになってる、ってとこ
ろかな
無駄な部分?
この構造体をメモリ上で見てみるとわかるかな

void Use_TEST_STRUCT_DOUBLE_2()
{
    TEST_STRUCT_DOUBLE_2 stTEST_STRUCT_DOUBLE_2;
    TRACE( "%X\n", &stTEST_STRUCT_DOUBLE_2 );
    // まずは 0xAA で初期化。
    memset
        ( &stTEST_STRUCT_DOUBLE_2
        , 0xAA
        , sizeof( stTEST_STRUCT_DOUBLE_2 ) 
        );
    // 各変数をセット。
    stTEST_STRUCT_DOUBLE_2.m_ch  = 63;
    stTEST_STRUCT_DOUBLE_2.m_d   = 2.0;
    stTEST_STRUCT_DOUBLE_2.m_ch2 = 63;
}

 memset() って? ランタイムっぽいけど
 memset() は、第1引数のアドレスから第3引数バイトまで、第2引数で
埋めるっていうランタイム。 TRACE() で構造体のアドレスを出してるから
それでメモリウィンドウを見てみて
ほい。ん? どこで止めて?
最初は

    stTEST_STRUCT_DOUBLE_2.m_ch  = 63;

で止めて、 memset() の結果を見た方がいいかな
ほい。お、全部 AA だ
 8 バイト単位で改行するとこうなります

AA AA AA AA AA AA AA AA
AA AA AA AA AA AA AA AA
AA AA AA AA AA AA AA AA

今度は、最後の } の所まで進めて
構造体にデータ入れ終わる所までね。ほい

3F AA AA AA AA AA AA AA
00 00 00 00 00 00 00 40
3F AA AA AA AA AA AA AA

だいぶ変わった
まず真ん中の行から見て。これは double だから 8 バイトまるまる使い
ます
だから AA がないんだね
一方、上下の char は 1 バイトしか使わないので、先頭の 1 バイトにし
かデータが入っていません
残りは AA のままなんだ
つまり、 char の変数として使っている後ろに、 7 バイトの無駄な領域
があるってこと。この無駄な領域を【パディング】って言います
パディング?
そう、 padding 、英語で〈詰め物〉って意味
なるほど。でもさ、これってホント、無駄じゃん。なんでこんなふうに
なってるの?
それは、この方がコンピューターにとっては見やすいから
見やすい?
 Version 4.08 ( No.058 ) でちょっと触れたけど、 int みたいに、コン
ピューターが処理しやすいサイズがあるんです
あ、32ビットだから 4 バイトだと処理しやすい、とかゆーあれね
そう。今はひとつの変数が最大 8 バイト単位になるようになっていて、
そうするとコンピューターは 4 の倍数だから処理が楽になるんです
そのための無駄ってわけね
この、サイズを揃えるっていう仕組みを【アライメント】って言います
あらいめんと?
 alignment 、〈整列〉って意味
確かに並べてるね
このアライメントの仕組みがあるから、構造体を作ると自動的にパディン
グが入れられて、サイズが倍数になるんです
質問!
はい火美ちゃん
さっき〈今はひとつの変数が最大 8 バイト単位に〉って言ってたってこ
とは、それって変えられるってこと?
そういうこと。これは【プロジェクトの設定】>【C/C++】
>【コード生成】ページの【構造体メンバのアライメント】ってところに
 8 バイトって書いてある!
これを 1 バイトにしてリビルドして、もう一度試してみて
ほい
まずサイズを見てみます

    TRACE( "%d\n", sizeof( TEST_STRUCT_DOUBLE_2 ) );
    // 10 

 10 ! char + double + char = 1 + 8 + 1 = 10 ?
そういうこと。メモリを見てみると、 memset() の直後は

AA
AA AA AA AA AA AA AA AA
AA

という感じになってます。その次にデータを入れてみると

3F
00 00 00 00 00 00 00 40
3F

うわー、パディングがまるまるなくなってる
このように、プロジェクトの設定でアライメントを変えると、構造体の中
身は大きく変わります
ここまで変わるんだ……質問!
はい火美ちゃん
結局、アライメントっていくつがいいの? デフォルトの 8 でいいの?
今はコンピューターの性能がかなりいいから、それほど大きな問題じゃな
いかも。 8 でもいいし 1 でもいいし。ただ
ただ?
今は CPU に比べて、メモリサイズやネットワークの太さのの方が問題に
なることが多いと思うから、そう考えるとパディング 1 の方がいいかも
 CPU はどんどん性能上がってるけどメモリとかまだ高いものね……

/*
    Preview Next Story!
*/
でも今回ってバイナリーとかあんま関係なくない?
実は、ファイルの読み書きが絡んでくると重要になるんです

アライメントを 8 で書き込んで 1 で読み込んだら?
あ……
というわけで次回
< Version 12.12 構造体のファイル読み書き >
につづく!
この辺はうっかりバグになりやすいから
んーでもホントにうっかりって気が……。
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。