構造体の動的確保

学生の人数を、書いた時点で決めたくない

前回まで Student はローカル変数として置いていました。ローカル変数はその関数を抜けた瞬間に消えるので、「学生を 1 人つくって返す関数」を素直に書くと壊れます。

Student *create_bad(void) { Student s; /* 関数の中だけで生きる */ s.score = 80; return &s; /* 抜けた瞬間に消えるものの番地を返している */ }

これは第 2 章で見たダングリングポインタです。返ってきた番地は、もう誰のものでもありません。中身を読めることもありますが、それはたまたま上書きされていないだけです。

malloc(sizeof(Student)) で構造体 1 つ分を借りる

ヒープから借りた領域は、free するまで生き続けます。誰の関数が終わろうと関係ありません。だから「つくって返す」にはヒープを使います。

Student *s = malloc(sizeof(Student));

sizeof(Student) は構造体 1 つ分のバイト数です。ここで sizeof(Student *) と書き間違えるのが定番の事故で、そう書くとポインタ 1 個分しか借りられません。構造体はそれより大きいので、s->name に名前を書いた時点で借りていない場所を壊します。コンパイラは通してしまうので、自分で気づくしかありません。

書き間違えを防ぐ書き方として、型ではなく変数から大きさを取る形があります。

Student *s = malloc(sizeof(*s));

*s は「s が指す先」つまり Student なので、意味は同じです。型名を二度書かないぶん、あとで型を変えてもずれません。

確保に失敗したら NULL が返る

malloc は借りられなかったとき NULL を返します。返ってきたポインタをそのまま -> で触ると、そこで落ちます。使う前に必ず確かめてください。

if (s == NULL) { printf("確保できませんでした\n"); return 1; }

借りたものには持ち主がいる

create_student の中で malloc して返す、という形にすると、確保した場所と解放する場所が別の関数になります。ここで大事なのは「返してもらった側が持ち主になり、free する責任も引き受ける」という約束をはっきりさせることです。C にはこれを強制する仕組みが無いので、関数の名前とコメントで示すしかありません。create_ で始まる名前なら呼び出し側が捨てる、という慣習はよく使われます。

解放したあとにポインタへ NULL を入れておくと、うっかり使ったときに「番地はあるが中身は他人のもの」ではなく、はっきり落ちるか、NULL 判定で弾けます。

free(s); s = NULL;

要件

  1. create_student は malloc で Student 1つ分を確保する
  2. 確保に失敗したら NULL を返す
  3. 名前は strcpy でコピーし、点数は代入する
  4. 表示のあとに free し、ポインタに NULL を入れる

入出力例

main("佐藤 80") → "佐藤 80 free後 NULL" main("高橋 64") → "高橋 64 free後 NULL"

ヒント

生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア
編集 LuaGate編集部
main.c
学習モード

メモ

構造体の動的確保

⌘S で保存