補足(いいわけ?)001


動的メモリ確保(2003/1/15)

掲示板の方で、「5.6 動的メモリ確保・連結リスト」(p.267)における BookData構造体の確保について、質問がありました。

数回やりとりしてみて、やはりこのあたりのことに関して 「体当り学習〜」は説明不足ではないか、と感じたので、 ここで補足説明します。

構造体のサイズについて

構造体は、いくつかのメンバをまとめて作る型ですので、 構造体のサイズ(sizeofして得られる値)は、メンバのサイズの和+αになります。

たとえば私の環境では、sizeof(int)は4なので、以下の構造体のサイズは8です。

typedef struct {
  int a;
  int b;
} Hoge;

このケースでは、構造体のサイズは、 ちょうどメンバのサイズの和になっていますね。

ただ、構造体メンバの間には「隙間」が空くこともあるので、 以下の例のように、きっちり合計サイズにならないことはあります。 上で「+α」と書いたのはそのためです。

    
typedef struct {
    char a;
    int b;
    float c;
    double d;
} Hoge;

うちの環境は、sizeof(char)は1、sizeof(int)は4, sizeof(float)も4, sizeof(double)は8なのですが、1+4+4+8の17にはならず、20になりました。

・List5-12版のBookData構造体について

そして、List5-12にあるBookData構造体↓は、

typedef struct {
    char        book_name[BOOK_NAME_LEN];       /* 書名 */
    char        author[AUTHOR_LEN];             /* 著者 */
    char        publisher[PUBLISHER_LEN];       /* 出版社 */
    int         price;                          /* 価格 */
    char        isbn[ISBN_LEN];                 /* ISBN */
    char        note[NOTE_LEN];                 /* 備考 */
} BookData;

charの配列を構造体メンバの中に含んでいます。

図にすると、以下のようなイメージです。

注)
図を見やすくするために、配列の要素数を少なくし、ISBNと備考は削りました。
また、文字が升目にぴったり合っていませんし、 日本語文字は2バイト食うことが多いのですが、 そのへんは「心の目」で見てください(^^;

ただし、この方法の問題は、

  1. 書名や著者名などに、文字数制限ができてしまう。
  2. 配列のサイズを大きくすることで文字数制限を緩くしようとすると、 構造体が巨大になる。

ということです。

また、List5-12では、この構造体を配列にすることで 「たくさんの本」を管理しています。 これもまた、配列のサイズにより、 管理できる本の数に制限をかけることになりますし、 サイズを大きくすることで制限を緩くしようとすると、 配列が巨大になります。 特に、BookData自体が大きいと、この問題は深刻です。

  • 動的メモリ確保版のBookData構造体について

    そこで動的メモリ確保を使って、上記の問題を解決するわけです。

    動的メモリ管理版のBookData構造体は、以下のようになっています。

    typedef struct BookData_tag {
        char        *book_name;     /* 書名 */
        char        *author;        /* 著者 */
        char        *publisher;     /* 出版社 */
        int         price;          /* 価格 */
        char        *isbn;          /* ISBN */
        char        *note;          /* 備考 */
        struct BookData_tag *next;  /* 次の要素を指すポインタ */
    } BookData;
    

    この構造体は、書名や著者名については、ポインタだけを保持しています。

    ポインタについては、「体当たり学習〜」ではp.190で説明していますが、 ちょっと大雑把な言い方をすれば「メモリ中のアドレス」のことです。 さらに乱暴な言い方をすると、要するに「intと似たようなもの」です。

    動的メモリ管理版のBookData構造体では、 書名や著者名そのものをメンバに含めることはしないで、 その先頭の文字へのポインタだけを持っていますから、図にするとこうなります。

    例によってISBNや備考は省略しています。

    こちらでは、BookData構造体自体は、さほど大きなサイズになりません。

    書名や著者名の領域は、その長さに合わせて別途malloc()しますから、 書名や著者名に文字列制限をかける必要はありません。 また、書名や著者名を長くしても、BookData構造体のサイズ自体は変わりません。 このことは、上の図を見れば明らかでしょう。

    ところで、BookData構造体をこのような形にすることで、上記の

    1. 書名や著者名などに、文字数制限ができてしまう。
    2. 配列のサイズを大きくすることで文字数制限を緩くしようとすると、 構造体が巨大になる。

    という問題は解決できますが、BookData構造体そのものを配列で管理すると、 やはり「管理できる本の数」に制限を作ってしまいますし、 配列を巨大にすることで制限を緩くしようとするとメモリが無駄になります。 とはいえ、BookData構造体そのものが小さくなっていますから、 BookData構造体の配列を巨大にしても、 List5-12版の時よりは問題が小さくなっていることは確かです。

    それを解決するために、List5-18では、 BookData構造体もmalloc()で確保して連結リストで管理しています。 それについては「体当たり学習〜」のp.278以降を参照してください。


    「補足(いいわけ?)」の目次に戻る | ひとつ前のページ | ひとつ後のページ | 「C言語 体当たり式徹底入門」のページに戻る | トップページに戻る