reallocで拡張する
動的配列のサイズを変更します。
「reallocで拡張する」はCoddyKit上の無料C Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C Academyコースには全4レッスンが含まれています。
mallocだけでは足りないとき
配列を確保して値を格納した後で、もっと広い領域が必要だとわかることがあります。末尾を越えて書き込むことはできません。
reallocを使うと、既存のヒープ領域のサイズを変更し、すでに格納されているデータを保持できます。
reallocのシグネチャ
realloc(ptr, new_bytes)には、元のポインターと新しい合計サイズ(バイト単位)を渡します。
新しいサイズの領域へのポインターが返されます。データの先頭部分は、元のサイズと新しいサイズの小さい方まで保持されます。
int *bigger = realloc(a, new_n * sizeof(*a));領域が移動することがある
reallocは領域をその場で拡張することもあれば、新しい領域を確保してデータをコピーし、古い領域を解放することもあります。
いずれの場合も、元のポインターは無効になる可能性があります。以降は、返されたポインターを使わなければなりません。
a = realloc(a, new_n * sizeof(*a));
/* the old value of a may no longer be valid */自己代入の落とし穴
a = realloc(a, ...)と書くと簡潔に見えますが危険です。reallocがNULLを返した場合、aが上書きされ、元のポインターを失って古い領域がリークします。
代わりに一時変数を使ってください。
int *tmp = realloc(a, new_n * sizeof(*a));
if (tmp == NULL) { /* a is still valid */ return 1; }
a = tmp;安全に配列を拡張する
このプログラムは3個のintを確保し、reallocで6個に拡張します。最初の3つの値は保持されます。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *a = malloc(3 * sizeof(*a));
if (!a) return 1;
for (int i = 0; i < 3; i++) a[i] = i + 1;
int *tmp = realloc(a, 6 * sizeof(*a));
if (!tmp) { free(a); return 1; }
a = tmp;
for (int i = 3; i < 6; i++) a[i] = i + 1;
for (int i = 0; i < 6; i++) printf("%d\n", a[i]);
free(a);
return 0;
}追加されたバイトは未初期化
領域を拡張すると、末尾に追加された領域には、新しくmallocで確保したメモリと同じように不定の値が入っています。
reallocは既存のデータを保持しますが、新しい領域をゼロ初期化することはありません。読み取る前に初期化してください。
a = realloc(a, 6 * sizeof(*a));
for (int i = 3; i < 6; i++) a[i] = 0; /* clear new slots */倍増戦略
要素を1つ追加するたびにreallocを呼び出すと低速です。典型的な方法は、空きがなくなるたびに容量を2倍にすることです。
これにより、配列の拡張中にreallocを呼び出す回数を少なくでき、平均的に高い性能が得られます。
if (count == cap) {
cap = cap ? cap * 2 : 4;
int *tmp = realloc(a, cap * sizeof(*a));
if (!tmp) { free(a); return 1; }
a = tmp;
}追加しながら配列を構築する
このプログラムは空の状態から始め、必要に応じて容量を2倍にしながら数値を追加します。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *a = NULL;
size_t count = 0, cap = 0;
for (int v = 1; v <= 5; v++) {
if (count == cap) {
cap = cap ? cap * 2 : 2;
int *tmp = realloc(a, cap * sizeof(*a));
if (!tmp) { free(a); return 1; }
a = tmp;
}
a[count++] = v;
}
for (size_t i = 0; i < count; i++) printf("%d\n", a[i]);
free(a);
return 0;
}realloc(NULL, n)はmallocと同じように動作する
ポインターにNULLを渡すと、reallocはmallocとまったく同じように動作します。
倍増ループの最初にa = NULLとしても動作するのはこのためです。最初のreallocが初期領域を確保します。
int *a = NULL;
a = realloc(a, 4 * sizeof(*a)); /* same as malloc(4 * sizeof(*a)) */領域を縮小する
reallocを使って領域を小さくすることもできます。新しいサイズまでのデータは保持され、それ以降は破棄されます。
縮小によってメモリが返される場合もありますが、実装によっては大きい領域を保持することもあります。
int *tmp = realloc(a, 2 * sizeof(*a)); /* down from more */
if (tmp) a = tmp;計算でのオーバーフローを避ける
容量が大きくなると、cap * sizeof(*a)の計算結果がsize_tの範囲を超え、小さな値にラップアラウンドする可能性があります。
非常に大きな配列では、乗算がオーバーフローしないか確認するか、利用できるシステムではreallocarrayを使用してください。
if (cap > SIZE_MAX / sizeof(*a)) { /* overflow */ return 1; }
int *tmp = realloc(a, cap * sizeof(*a));クイックチェック
reallocについての理解度を確認しましょう。
まとめ
reallocはヒープブロックのサイズを変更し、別の場所へ移動する場合があるため、必ず返されたポインターを使用してください。
失敗しても元のブロックをリークさせないよう、一時変数に代入します。速度を上げるには容量を2倍ずつ増やし、新しいスロットを初期化し、realloc(NULL, n)はmallocと同じであることを覚えておいてください。次は、このロジックを再利用可能なベクター型でラップします。
よくある質問
「reallocで拡張する」レッスンは無料ですか?
はい。「reallocで拡張する」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C Academyコースには全4レッスンが含まれています。
「reallocで拡張する」で何を学びますか?
動的配列のサイズを変更します。 ブラウザで直接実行するハンズオンコードでC Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「reallocで拡張する」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC Academyレッスンでコードを書いて実行できますか?
はい。すべてのC Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 配列を確保する
- reallocで拡張する
- 再利用可能なベクター型
- 解放してリークを防ぐ