構造体の動的確保
学生の人数を、書いた時点で決めたくない
前回まで 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;要件
- create_student は malloc で Student 1つ分を確保する
- 確保に失敗したら NULL を返す
- 名前は strcpy でコピーし、点数は代入する
- 表示のあとに free し、ポインタに NULL を入れる
入出力例
main("佐藤 80") → "佐藤 80
free後 NULL"
main("高橋 64") → "高橋 64
free後 NULL"