callocとrealloc
malloc した直後の中身は決まっていない
malloc が返すのは「借りた場所」であって、中身は前に誰かが使った値のままです。何が入っているかは決まっていないので、初期化せずに読んではいけません。合計を求めるつもりで total += scores[i]; と書いて、一度も値を入れていなければ、結果は毎回変わります。
そこで、0で埋めた状態で借りたいときに使うのが calloc です。
calloc は0で埋めてくれる
int *scores = calloc(n, sizeof(int));引数が2つに分かれていて、個数と1個あたりのバイト数を渡します。malloc(sizeof(int) * n) と借りる大きさは同じですが、calloc は全バイトを0で埋めることが規格で決まっています。int なら0、double なら0.0になります。集計用の配列のように「まず0から始めたい」ものはこちらが向いています。
なお、ポインタを0で埋めた結果が NULL になるかどうかは規格の上では保証されていません。実際の環境ではそうなることがほとんどですが、ポインタの配列は明示的に NULL を入れるほうが確実です。
realloc であとから伸ばす
人数が増えて、借りた場所が足りなくなったとします。もう一度 malloc して手で写す方法もありますが、それを1回でやってくれるのが realloc です。
int *tmp = realloc(scores, sizeof(int) * (n + m));渡すのは今のポインタと新しい合計バイト数です。追加ぶんではなく合計であることに注意してください。元の内容はそのまま引き継がれ、増えた部分の中身は決まっていません。calloc と違って0にはなりません。
戻り値を元の変数に直接入れない
realloc で一番危ないのはここです。
scores = realloc(scores, sizeof(int) * (n + m)); /* これは駄目 */失敗すると realloc は NULL を返しますが、そのとき元の領域はまだ生きています。上のように書くと scores が NULL で上書きされ、元の領域のアドレスを失います。解放もできず、中身も取り出せません。
ですから、いったん別の変数で受けて、成功を確かめてから入れ替えます。
int *tmp = realloc(scores, sizeof(int) * (n + m));
if (tmp == NULL) {
free(scores);
return 1;
}
scores = tmp;もうひとつ覚えておくことがあります。realloc は、その場で伸ばせないときは新しい場所に確保して中身を写し、古い場所を解放します。つまり返ってくるアドレスは元と同じとは限りません。どちらになるかは実装しだいなので、古いポインタを別の変数に取っておいて後から使う、という書き方は成立しません。入れ替えたら古いほうは忘れてください。
今回は calloc で0から始め、realloc で人数を追加します。
要件
- 最初の確保は calloc を使い、0で埋まっていることを表示で示す
- realloc の戻り値はいったん別の変数で受け、NULL でないことを確かめてから入れ替える
- realloc に渡すのは追加ぶんではなく新しい合計バイト数にする
- 最後に free する
入出力例
main("3 2
80 90 70
60 100") → "calloc直後 0 0 0
拡張後 80 90 70 60 100
合計 400点"
main("2 1
55 65
75") → "calloc直後 0 0
拡張後 55 65 75
合計 195点"