これまでのステップでは、プログラムの開始時に確保されるメモリ(静的メモリ確保)を中心に学んできました。しかし、プログラムを実行している途中で必要なメモリサイズが決まる場合や、実行状況によって必要なメモリ量が変わる場合には、静的メモリ確保だけでは対応が難しいことがあります 🤔。
そこで登場するのが動的メモリ確保です!これは、プログラムの実行中に必要な量のメモリを確保したり、不要になったメモリを解放したりする仕組みです。C言語では、主にmalloc
、calloc
、realloc
という標準ライブラリ関数を使って動的メモリ確保を行います。これらの関数は <stdlib.h>
ヘッダファイルで定義されています。
動的に確保されたメモリは、自動的には解放されません。不要になったら必ず free()
関数を使って解放する必要があります。これを忘れるとメモリリークの原因となり、プログラムがメモリを使い果たしてしまう可能性があります 😨。
Warning: Undefined array key “is_admin” in
/home/c2261046/public_html/omomuki-tech.com/wp-content/themes/sango-theme/library/gutenberg/dist/classes/Toc.php on line
113
Warning: Undefined array key “is_category_top” in
/home/c2261046/public_html/omomuki-tech.com/wp-content/themes/sango-theme/library/gutenberg/dist/classes/Toc.php on line
118
Warning: Undefined array key “is_top” in
/home/c2261046/public_html/omomuki-tech.com/wp-content/themes/sango-theme/library/gutenberg/dist/classes/Toc.php on line
124
1. malloc: メモリを確保する基本関数
malloc
関数は、指定されたバイト数のメモリブロックを
ヒープ領域と呼ばれるメモリ空間に確保します。確保されたメモリ領域の先頭アドレスを
void*
型のポインタとして返します。
#include <stdlib.h>
void* malloc(size_t size);
size
: 確保したいメモリのバイト数。通常、sizeof
演算子を使って必要なサイズを指定します。
- 戻り値: 確保されたメモリ領域の先頭アドレス (
void*
型)。確保に失敗した場合はNULL
ポインタを返します。
malloc
が返すポインタはvoid*
型なので、実際に使用する際には目的のデータ型へのキャスト(型変換)が必要です。
⚠️
注意点:
- 確保されたメモリの内容は初期化されません(不定な値が入っています)。
- メモリ確保に失敗すると
NULL
を返すため、必ず戻り値をチェックしましょう。
使用例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
int n = 5; // 例えば、整数5個分のメモリを確保
// int型5個分のメモリを確保 (sizeof(int) * 5 バイト)
ptr = (int*)malloc(n * sizeof(int));
// メモリ確保が成功したかチェック
if (ptr == NULL) {
fprintf(stderr, "メモリの確保に失敗しました。\n");
return 1; // エラー終了
}
printf("メモリ確保成功!アドレス: %p\n", (void*)ptr);
// 確保したメモリに値を代入
for (int i = 0; i < n; ++i) {
ptr[i] = i * 10;
printf("ptr[%d] = %d\n", i, ptr[i]);
}
// ★重要: 不要になったメモリを解放
free(ptr);
printf("メモリを解放しました。\n");
return 0;
}
2. calloc: 初期化されたメモリを確保する関数
calloc
関数もメモリを確保する関数ですが、
malloc
とは少し異なります。指定された
要素数と
要素ごとのサイズに基づいてメモリを確保し、その領域全体を
ゼロ(0)で初期化します ✨。配列のように、同じサイズの要素を複数確保する場合に便利です。
#include <stdlib.h>
void* calloc(size_t nmemb, size_t size);
nmemb
: 確保したい要素の数。
size
: 各要素のサイズ(バイト単位)。
- 戻り値: 確保・初期化されたメモリ領域の先頭アドレス (
void*
型)。確保に失敗した場合はNULL
ポインタを返します。
malloc
と同様に、戻り値は適切な型にキャストして使用します。
⚠️
注意点:
- メモリ確保に失敗すると
NULL
を返すため、必ず戻り値をチェックしましょう。
malloc
と異なり、確保したメモリ領域は自動的に0で初期化されます。
- 初期化処理が入るため、
malloc
より若干遅くなる可能性があります。
使用例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
int n = 5; // 例えば、整数5個分のメモリを確保
// int型5個分のメモリを確保し、0で初期化
ptr = (int*)calloc(n, sizeof(int));
// メモリ確保が成功したかチェック
if (ptr == NULL) {
fprintf(stderr, "メモリの確保に失敗しました。\n");
return 1; // エラー終了
}
printf("メモリ確保成功(初期化済み)!アドレス: %p\n", (void*)ptr);
// 確保したメモリの内容を確認(すべて0のはず)
for (int i = 0; i < n; ++i) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}
// ★重要: 不要になったメモリを解放
free(ptr);
printf("メモリを解放しました。\n");
return 0;
}
3. realloc: 確保済みのメモリサイズを変更する関数
realloc
関数は、すでに
malloc
や
calloc
で確保したメモリブロックのサイズを変更するために使用します。元のデータ内容を保持したまま、メモリサイズを拡大したり縮小したりできます 🔄。
#include <stdlib.h>
void* realloc(void* ptr, size_t size);
ptr
: サイズを変更したい、既存のメモリブロックへのポインタ。NULL
を指定した場合、malloc(size)
と同じ動作になります。
size
: 新しく確保したいメモリのバイト数。0を指定した場合の動作は処理系に依存しますが、一般的にはメモリが解放されNULL
が返されるか、最小サイズのブロックが割り当てられます(free(ptr)
を使うのが安全です)。
- 戻り値: サイズ変更されたメモリ領域の先頭アドレス (
void*
型)。
- サイズ変更に成功した場合、新しいアドレス(または元の
ptr
と同じアドレス)を返します。元のptr
とは異なるアドレスが返される可能性がある点に注意してください。
- サイズ変更に失敗した場合、
NULL
ポインタを返します。この場合、元のptr
が指すメモリブロックは解放されず、そのまま有効です。
⚠️
注意点:
- 戻り値を必ず別のポインタ変数で受け取るようにしましょう。もし
realloc
が失敗してNULL
を返した場合、元のポインタ(ptr
)に直接代入してしまうと、元のメモリブロックへのアクセス手段を失ってしまいます(メモリリーク)。
- メモリを拡大した場合、追加された領域の内容は初期化されません(不定な値が入っています)。
- メモリの再確保が現在の場所でできない場合、
realloc
は新しい場所にメモリブロックを確保し、元の内容をコピーしてから古いブロックを解放します。そのため、アドレスが変わる可能性があります。
使用例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
int n1 = 5;
int n2 = 10; // サイズを増やす
// 最初にint型5個分のメモリを確保
ptr = (int*)malloc(n1 * sizeof(int));
if (ptr == NULL) {
fprintf(stderr, "最初のメモリ確保に失敗しました。\n");
return 1;
}
printf("最初に確保したメモリのアドレス: %p\n", (void*)ptr);
for(int i=0; i="" for="" fprintf(stderr,="" free(ptr);="" i="" if="" int="" n2="" n2;="" printf("サイズ変更後のメモリのアドレス:="" printf("サイズ変更後の内容:\n");="" ptr="temp_ptr;" ptr[i]="i;" realloc失敗時は元のptrは有効なので、解放処理が必要="" return="" sizeof(int));="" {="" }="" ★重要:="" サイズ変更が成功したかチェック="" メモリサイズをint型10個分に変更="" 値を代入="" 成功したら、新しいポインタを元の変数に代入="" 戻り値は一時的なポインタ変数で受け取る="" 拡張された部分も含めて値を代入・表示="" 拡張部分="">= n1) は初期化されていない
if(i >= n1) ptr[i] = i * 100; // 新しく値を代入
printf("ptr[%d] = %d\n", i, ptr[i]);
}
// ★重要: 不要になったメモリを解放
free(ptr);
printf("メモリを解放しました。\n");
return 0;
}
4. まとめと使い分け
malloc
, calloc
, realloc
は、状況に応じて柔軟なメモリ管理を可能にする強力なツールです 💪。それぞれの特徴を理解し、適切に使い分けることが重要です。
関数 |
主な用途 |
初期化 |
引数 |
注意点 |
malloc |
指定バイト数のメモリ確保 |
しない |
確保する合計バイト数 |
内容は不定値。NULLチェック必須。 |
calloc |
配列など、複数要素のメモリ確保 |
する (0で初期化) |
要素数、要素1つのサイズ |
初期化が必要な場合に便利。NULLチェック必須。 |
realloc |
確保済みメモリのサイズ変更 |
しない(拡張部分) |
元のポインタ、新しい合計バイト数 |
アドレスが変わる可能性あり。失敗時のNULLチェックと元のポインタの扱い(一時変数で受ける)。 |
これらの関数を使う上で最も重要なことは、確保したメモリは必ずfree()
で解放することです。動的メモリ確保は非常に便利ですが、使い方を誤るとメモリリークや不正アクセスといった深刻なバグの原因となります。次のステップでは、メモリ解放free()
について詳しく学びます。
参考情報
より詳しい情報や関数の仕様については、以下のリファレンスサイトなどを参照してください。