C言語 malloc プログラミングのサンプルコード:初心者向け解説
C言語では、スタックメモリとヒープメモリの2種類のメモリ領域が存在します。
- ヒープメモリ
プログラム実行中に動的に割り当て and 解放できるメモリ領域です。malloc()などの関数を使用して管理されます。 - スタックメモリ
関数呼び出しごとに自動的に割り当てられ解放されるメモリ領域です。変数や関数の引数などが格納されます。
動的メモリ管理とは、プログラム実行中に必要に応じてメモリ領域を確保 and 解放することです。これは、ヒープメモリを使用して行われます。
malloc() 関数
malloc() 関数は、ヒープメモリから指定されたサイズの領域を割り当て、その領域の先頭アドレスをポインタとして返します。
void *malloc(size_t size);
size_t
: 確保するメモリ領域のサイズ(バイト単位)
malloc() 関数は成功した場合、メモリ領域の先頭アドレスをポインタとして返します。メモリ領域の確保に失敗した場合には、NULLポインタを返します。
メモリ領域を割り当てた後は、必ず free()
関数を使用して解放する必要があります。
void free(void *ptr);
ptr
: 解放するメモリ領域の先頭アドレスをポインタとして渡します
free() 関数は、メモリ領域を解放し、他のプログラムで使用できるようにします。
malloc() の例
以下は、malloc() 関数を使用して整数を格納するためのメモリ領域を割り当て、その値を設定し、メモリ領域を解放する例です。
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p;
// 10バイトのメモリ領域を割り当て
p = malloc(sizeof(int));
if (p == NULL) {
printf("メモリ領域の確保に失敗しました。\n");
return 1;
}
// メモリ領域に値を設定
*p = 10;
// メモリ領域の内容を出力
printf("p = %d\n", *p);
// メモリ領域を解放
free(p);
return 0;
}
このプログラムを実行すると、以下の出力が表示されます。
p = 10
- malloc() 関数は、ヒープメモリのみにメモリを割り当てます。スタックメモリ領域にメモリを割り当てることはできません。
- malloc() 関数は、連続したメモリ領域を確保します。配列などの非連続なメモリ領域を確保する場合は、
calloc()
関数などの他の関数を使用する必要があります。 - malloc() 関数は、必ず
free()
関数を使用して解放する必要があります。解放しないと、メモリリークが発生する可能性があります。
malloc() 関数は、C言語において動的メモリ管理を行うための基本的な関数です。メモリ領域を適切に割り当て and 解放することで、メモリリークやプログラムクラッシュなどの問題を回避することができます。
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int num;
char name[32];
} person_t;
int main() {
person_t *p;
// person_t 構造体サイズのメモリ領域を割り当て
p = malloc(sizeof(person_t));
if (p == NULL) {
printf("メモリ領域の確保に失敗しました。\n");
return 1;
}
// 構造体のメンバに値を設定
p->num = 1;
strcpy(p->name, "Taro Yamada");
// 構造体の内容を出力
printf("番号: %d\n", p->num);
printf("名前: %s\n", p->name);
// メモリ領域を解放
free(p);
return 0;
}
番号: 1
名前: Taro Yamada
このコードでは、まず person_t
構造体を定義します。この構造体は、num
という整型メンバと name
という文字列メンバを持ちます。
次に、malloc()
関数を使用して person_t
構造体サイズのメモリ領域を割り当てます。メモリ領域の確保に成功すると、p
ポインタにはメモリ領域の先頭アドレスが格納されます。
その後、p->num
に 1 を代入し、strcpy()
関数を使用して p->name
に "Taro Yamada" という文字列をコピーします。
最後に、free()
関数を使用してメモリ領域を解放します。
このコードは、構造体へのポインタを割り当て、構造体のメンバに値を設定し、メモリを解放する方法を示す基本的な例です。実際のプログラムでは、状況に応じてコードを適宜変更する必要があります。
- 配列へのポインタの割り当てとメモリ解放
int main() {
int *p;
int n = 10;
// n 個の整数を格納できる配列へのポインタを割り当て
p = malloc(sizeof(int) * n);
if (p == NULL) {
printf("メモリ領域の確保に失敗しました。\n");
return 1;
}
// 配列の要素に値を設定
for (int i = 0; i < n; i++) {
p[i] = i + 1;
}
// 配列の内容を出力
for (int i = 0; i < n; i++) {
printf("%d ", p[i]);
}
printf("\n");
// メモリ領域を解放
free(p);
return 0;
}
- 二次元配列へのポインタの割り当てとメモリ解放
int main() {
int **p;
int m = 3;
int n = 5;
// m 行 n 列の二次元配列へのポインタを割り当て
p = malloc(sizeof(int *) * m);
for (int i = 0; i < m; i++) {
p[i] = malloc(sizeof(int) * n);
if (p[i] == NULL) {
for (int j = 0; j < i; j++) {
free(p[j]);
}
free(p);
printf("メモリ領域の確保に失敗しました。\n");
return 1;
}
}
// 二次元配列の要素に値を設定
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
p[i][j] = i * n + j;
}
}
// 二次元配列の内容を出力
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
printf("%d ", p[i][j]);
}
printf("\n");
}
// メモリ領域を解放
for (
calloc()
calloc()
関数は、malloc()
と同様にヒープメモリから領域を確保しますが、以下の点が異なります。
- 2つの引数を持つ:
- 確保する要素の数
- 各要素のサイズ
- 確保した領域を初期化 (0 で埋める)
void *calloc(size_t nmemb, size_t size);
calloc()
は、構造体や配列などのメモリ領域を初期化して確保する場合に便利です。
例
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p;
// 10個の整数を格納できる配列を初期化して確保
p = calloc(10, sizeof(int));
if (p == NULL) {
printf("メモリ領域の確保に失敗しました。\n");
return 1;
}
// 配列の内容を出力
for (int i = 0; i < 10; i++) {
printf("%d ", p[i]);
}
printf("\n");
// メモリ領域を解放
free(p);
return 0;
}
realloc()
realloc()
関数は、既に malloc
や calloc
で確保したメモリ領域のサイズを変更します。
void *realloc(void *ptr, size_t size);
size
: 変更後のメモリ領域のサイズptr
: サイズを変更するメモリ領域の先頭アドレスをポインタとして渡します
realloc()
は、メモリ領域のサイズを動的に変更する必要がある場合に便利です。
例
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p;
int n = 5;
// n 個の整数を格納できる配列を確保
p = malloc(sizeof(int) * n);
if (p == NULL) {
printf("メモリ領域の確保に失敗しました。\n");
return 1;
}
// 配列の内容を出力
for (int i = 0; i < n; i++) {
p[i] = i + 1;
}
printf("\n");
// 配列のサイズを 10 に変更
p = realloc(p, sizeof(int) * 10);
if (p == NULL) {
printf("メモリ領域のサイズ変更に失敗しました。\n");
return 1;
}
// 追加された要素に値を設定
for (int i = n; i < 10; i++) {
p[i] = i + 1;
}
// 配列の内容を出力
for (int i = 0; i < 10; i++) {
printf("%d ", p[i]);
}
printf("\n");
// メモリ領域を解放
free(p);
return 0;
}
自作のメモリ管理機構
より高度なメモリ管理が必要な場合は、malloc
や realloc
などの標準ライブラリの関数ではなく、自作のメモリ管理機構を使用することもできます。これは、メモリ使用量をより細かく制御したり、特定のメモリ割り当てアルゴリズムを使用したりする場合に役立ちます。
ただし、自作のメモリ管理機構は複雑になりやすく、バグが発生しやすいことに注意する必要があります。
上記以外にも、以下のような選択肢があります。
- C++ Standard Library: C++ Standard Libraryには、
std::vector
やstd::map
などのコンテナクラスが含まれています。これらのクラスは、動的メモリ管理を自動で行うため、malloc
やcalloc
を直接使用するよりも使いやすく、安全です。 - New operator (C++): C++では、
new
演算子を使用して動的メモリ確保を行うことができます。new
演算子は、malloc()
と同様にメモリ領域を確保しますが、型安全性を向上させるなどの利点があります。