mallocとfree
実行してみるまで人数が分からない
入門で書いた成績管理は int scores[100]; のような固定長の配列でした。この書き方は、要素数をコンパイルの時点で決めなければなりません。10人しか来なくても100人分の場所を取り、101人来たら破綻します。
人数を実行時に読み取ってから、その人数ぶんだけ場所を借りたい。そのための道具が malloc です。前回まで見てきた3つの領域のうち、ヒープを使います。
malloc が返すもの
malloc は <stdlib.h> にあり、必要なバイト数を渡すと、その大きさの領域の先頭アドレスを返します。
int n = 3;
int *scores = malloc(sizeof(int) * n);渡すのは要素数ではなくバイト数です。int が3個なら sizeof(int) * 3 で12バイトになります。sizeof(int) * n と書いておけば型を変えても壊れないので、数字の 12 を直接書かないでください。
返ってくるのは void * という「型の決まっていないポインタ」で、代入する変数の型に合わせて自動で変換されます。C言語では (int *) のキャストは不要です。
NULL を確かめる
借りられなかったときは NULL が返ります。この確認を飛ばして使うと、失敗した瞬間ではなく少し先で落ち、原因が分からなくなります。
if (scores == NULL) {
printf("確保に失敗しました\n");
return 1;
}確保した領域は普通の配列のように使える
scores はポインタですが、第1章でやったとおり scores[i] と書けます。*(scores + i) と同じ意味です。違うのは大きさが実行時に決まることと、後始末が自分の仕事になることだけです。
free は「消す」ではなく「返す」
使い終わったら free(scores); で返します。ここで起きるのは「借りていた領域を、また誰かに貸してよい状態に戻す」ことです。中の値が0で塗り潰されるわけではなく、scores に入っていたアドレスが消えるわけでもありません。
だから free のあとの scores は、もう自分のものではない場所を指したままになっています。これが前回出てきたダングリングポインタです。読めてしまうことがあるので、たちが悪いのも同じです。
そこで、返した直後に NULL を入れておきます。
free(scores);
scores = NULL;こうしておけば、うっかり触ったときに「たまたま動く」ではなく確実に落ちるので、間違いがその場で分かります。free(NULL) は何もしない安全な動作と決まっているので、二重に free してしまう事故も防げます。
今回は人数を読み取ってから、その人数ぶんの点数を動的に確保して集計します。
要件
- malloc で n 人分の int を確保する。バイト数は sizeof(int) * n で求める
- 戻り値が NULL のときは「確保に失敗しました」と出して return 1 する
- 平均は %.1f で小数第1位まで出す
- free したあと変数に NULL を入れ、最後の行を表示する
入出力例
main("3
80 90 79") → "人数 3
確保 12 バイト
合計 249点
平均 83.0点
解放後 scores は NULL です"
main("5
60 71 80 90 95") → "人数 5
確保 20 バイト
合計 396点
平均 79.2点
解放後 scores は NULL です"