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)); /* これは駄目 */

失敗すると reallocNULL を返しますが、そのとき元の領域はまだ生きています。上のように書くと scoresNULL で上書きされ、元の領域のアドレスを失います。解放もできず、中身も取り出せません。

ですから、いったん別の変数で受けて、成功を確かめてから入れ替えます。

int *tmp = realloc(scores, sizeof(int) * (n + m)); if (tmp == NULL) { free(scores); return 1; } scores = tmp;

もうひとつ覚えておくことがあります。realloc は、その場で伸ばせないときは新しい場所に確保して中身を写し、古い場所を解放します。つまり返ってくるアドレスは元と同じとは限りません。どちらになるかは実装しだいなので、古いポインタを別の変数に取っておいて後から使う、という書き方は成立しません。入れ替えたら古いほうは忘れてください。

今回は calloc で0から始め、realloc で人数を追加します。

要件

  1. 最初の確保は calloc を使い、0で埋まっていることを表示で示す
  2. realloc の戻り値はいったん別の変数で受け、NULL でないことを確かめてから入れ替える
  3. realloc に渡すのは追加ぶんではなく新しい合計バイト数にする
  4. 最後に 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点"

ヒント

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

メモ

callocとrealloc

⌘S で保存