C 言語動的メモリ割り当て: malloc、calloc、realloc、free をマスター
C 言語の動的メモリ割り当てに入る前に、言語におけるメモリ管理の仕組みを理解することが不可欠です。
C 言語におけるメモリ管理の仕組み
プリミティブ型で変数を宣言すると、コンパイラは自動的に スタック 上に領域を確保します。例えば float は通常 4 バイト (プラットフォーム依存) を占有します。sizeof 演算子で確認できます:
#include <stdio.h>
int main() {
float x;
printf("The size of float is %d bytes", sizeof(x));
return 0;
}
出力:
The size of float is 4 bytes
指定サイズの配列もスタック上に連続して割り当てられ、各要素はその型のサイズを占有します:
#include <stdio.h>
int main() {
float arr[10];
printf("The size of the float array with 10 elements is %d", sizeof(arr));
return 0;
}
結果:
The size of the float array with 10 elements is 40
スタック割り当ては自動的ですが、多くのプログラムでは実行時までサイズがわからないメモリを必要とします。このニーズに応えるのが 動的メモリ割り当て で、スタックではなく ヒープ を使用します。
C 言語の動的メモリ割り当て
動的割り当てでは、必要に応じて手動でメモリを要求・解放し、ポインタでヒープ上の割り当てブロックを追跡します。C 標準ライブラリはいくつかの関数を提供します:
- malloc() – 指定バイト数の連続ブロックを割り当て、先頭バイトへのポインタを返す。
- calloc() – 同サイズの要素配列を割り当て、全バイトをゼロ初期化し、先頭要素へのポインタを返す。
- realloc() – 既存割り当てブロックのサイズを変更し、内容を保持。
- free() – 以前に割り当てられたブロックをヒープに解放。
各関数を詳細に見ていきます。
malloc() 関数
malloc() は "memory allocation" の略。指定サイズのメモリブロックを確保し、そのポインタを返します。ポインタは通常目的の型にキャストして使用:
ptr = (cast_type *) malloc(byte_size);
例:
#include <stdlib.h>
int main() {
int *ptr;
ptr = malloc(15 * sizeof(*ptr)); /* 15 個の整数 */
if (ptr != NULL) {
*(ptr + 5) = 480; /* 6 番目の要素 */
printf("Value of the 6th integer is %d", *(ptr + 5));
}
}
出力:
Value of the 6th integer is 480
重要なポイント:
sizeof(*ptr)を使用すると型変更に対して堅牢。- 戻り値が
NULLかチェックして割り当て失敗を検知。 - 割り当てメモリは連続。配列として扱いポインタ演算でアクセス可能。
動的割り当てはプリミティブ型に限らず、構造体、文字配列などにも適用可能。
free() 関数
malloc()、calloc()、realloc() で割り当てたメモリは、リークを防ぐため明示的に解放する必要があります。free() がこの役割を果たします:
#include <stdio.h>
int main() {
int* ptr = malloc(10 * sizeof(*ptr));
if (ptr != NULL) {
*(ptr + 2) = 50;
printf("Value of the 2nd integer is %d", *(ptr + 2));
}
free(ptr);
}
出力:
Value of the 2nd integer is 50
calloc() 関数
calloc() は同サイズの複数ブロックを割り当て、全バイトをゼロ初期化するため、ゼロ初期化が必要な配列や構造体に最適です:
ptr = (cast_type *) calloc(n, size);
例 – 等差数列の合計:
#include <stdio.h>
int main() {
int i, *ptr, sum = 0;
ptr = calloc(10, sizeof(int));
if (ptr == NULL) {
printf("Error! memory not allocated.");
exit(0);
}
for (i = 0; i < 10; ++i) {
*(ptr + i) = i;
sum += *(ptr + i);
}
printf("Sum = %d", sum);
free(ptr);
return 0;
}
結果:
Sum = 45
calloc() vs malloc()
主な違い:
- 初期化:
calloc()はメモリをゼロクリア。malloc()は未初期化 (ガベージ値)。 - 用途: ゼロ初期化が必要な配列/構造体には
calloc()、初期化不要ならmalloc()がシンプル。 - パフォーマンス: 両者はほぼ同等だが、
calloc()はゼロクリア分わずかに遅い可能性。
realloc() 関数
realloc() は既存割り当てのサイズを変更し、内容を保持します:
ptr = realloc(ptr, newsize);
例 – 文字列バッファの拡張:
#include <stdio.h>
int main() {
char *ptr = malloc(10);
strcpy(ptr, "Programming");
ptr = realloc(ptr, 20); // 拡大
strcat(ptr, " In 'C'");
printf("%s, Address = %u", ptr, ptr);
free(ptr);
return 0;
}
再割り当て失敗時、realloc() は NULL を返し、元のブロックは有効なままです。
動的配列
動的配列は実行中に要素数を増やせるため、アルゴリズムやデータ構造で一般的なパターンです:
#include <stdio.h>
int main() {
int *arr_dynamic = NULL;
int elements = 2, i;
arr_dynamic = calloc(elements, sizeof(int));
for (i = 0; i < elements; i++) arr_dynamic[i] = i;
for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
elements = 4;
arr_dynamic = realloc(arr_dynamic, elements * sizeof(int));
printf("After realloc\n");
for (i = 2; i < elements; i++) arr_dynamic[i] = i;
for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
free(arr_dynamic);
}
結果:
arr_dynamic[0]=0 arr_dynamic[1]=1 After realloc arr_dynamic[0]=0 arr_dynamic[1]=1 arr_dynamic[2]=2 arr_dynamic[3]=3
まとめ
- 動的メモリを使用すると実行時にヒープ領域を割り当て/解放可能。
- 単一ブロックには
malloc()、ゼロ初期化配列にはcalloc()を使用。 - 常に
NULLチェックで割り当て失敗を検知。 free()でメモリを解放し、リークを防止。- より多くの領域が必要な場合は
realloc()でリサイズ。 - 動的配列は柔軟なデータ構造の強力なパターン。
C言語
- ROM、PROM、EPROM、EEPROMの理解:設計、プログラミング、識別
- C# 入出力の基礎:ユーザー入力と出力の基本をマスター
- C# usingステートメント完全ガイド:名前空間インポート、エイリアス、静的ディレクティブ
- C++メモリ管理の極意:newとdeleteのエキスパート活用法
- C言語ユーザー定義関数の4つの実証済みパターン
- C言語 動的メモリ確保: malloc, calloc, realloc, free をマスターする
- C言語 標準ライブラリ関数: 実践ガイド
- C++動的配列の確保をマスター:コード例付き実践ガイド
- C++関数を実例で徹底解説:基本から応用まで
- C言語の関数マスター:構造、宣言、ベストプラクティス
- C言語動的メモリ管理:calloc、malloc、freeの解説