第31回
データ構造(10)~構造体をポインタでつなぐ

必要なときに構造体を生成する

同じ構造体型のデータを複数扱う場合、必要なときに構造体のためのメモリ領域を確保する――という仕組みを使うと効率的です。

同じ種類の情報を連続して扱う

データの「型」とは、プログラムで扱う情報の「種類」を意味します。情報の「種類」とは、整数なのか、小数点を含む精緻な値なのか、あるいは文字や文字列なのか……という違いです。

一般の変数では『1種類の情報』しか扱えません。ところが構造体は、複数の型──異なる様々な種類の情報をひとまとめにして扱えます。

さらにプログラムでは、1種類の情報をただ1つだけ扱うことはまずありません。実務的な処理では、同じ型を持つ複数の変数──処理の中で意味的に共通する情報のかたまり──を複数、連続して扱うのが一般的です。

通常の変数では、それが配列となります。例えば受験者100人の試験の点数は、以下のような配列で表現できるでしょう
int score[100];

受験番号と点数はワンセット

しかしこのような単純な配列では、前回も触れたように『ただ100個の点数が並んだだけ』でしかありません。「受験番号何番の人の点数が何点だったのか?」を明確にするためには、受験番号を示す値とその人の点数とが関連していなければなりません。

そこで、構造体を使って以下のように2つの変数──受験番号と点数とをまとめることになります。
struct _result {
  int id;
  int score;
};

さらに100人分のデータを扱うなら、以下のように構造体型変数の配列を宣言することになるでしょう。
struct _result result[100]

メモリを動的に確保する関数~malloc

ただ、配列は予め要素数を指定しなければならないため、いろいろと不都合が生じます。上の例の場合、受験者が100人以下だとメモリの無駄遣いになりますし、100人以上だったら要素が足りなくなってしまいます。

そんなとき、『必要なときに必要なだけ構造体のメモリを確保』すれば効率的で無駄のない処理が実現します。これを(メモリの)「動的な確保」と呼びます。

メモリを確保するにはmalloc関数を使います ※1 。書式は以下のようになります。
<戻り値> = malloc(<サイズ>)

<サイズ>には確保したいバイト数を与えます。すると、malloc関数は指定したバイト数分のメモリを確保し、先頭のアドレスをvoid型のポインタとして返します。void型は『型を持たない型』で、どのような型のポインタにも代入できます。メモリを確保できなかった場合にはNULLポインタを返します。
メモリを確保する関数には、mallocの他にcallocがあります。両者の違いは、mallocが確保したメモリ領域を初期化しないのに対して、callocは確保後にメモリを初期化する点です

構造体のメモリ領域を確保する

構造体の占有するバイト数は定義によって異なるため、一定ではありません。そこで、『型の占有するバイト数』を調べるsizeof演算子を使い、以下のようにして1個の構造体に必要なメモリ』を確保します。
malloc(sizeof<構造体型名>);

上述の“struct _result”型に必要なメモリを確保し、その先頭ポインタを受け取るなら以下のようなソースを記述します。
struct _result {
  int id;
  int score;
};
struct _result *p; ---- 構造体型のポインタを宣言
p = malloc(sizeof(struct _result));