[C言語] mallocを使って可変長配列を実装する方法を解説
C言語では、malloc
関数を使用して動的にメモリを割り当てることで可変長配列を実装できます。
malloc
は指定したバイト数のメモリをヒープ領域から確保し、その先頭アドレスを返します。
このアドレスをポインタに保存し、配列のようにインデックスを使用して要素にアクセスできます。
メモリの再割り当てが必要な場合は、realloc
関数を使用して既存のメモリブロックを拡張または縮小します。
使用後はfree
関数でメモリを解放し、メモリリークを防ぐことが重要です。
- mallocを使った可変長配列の基本的な実装方法
- 動的にメモリを確保し、配列のサイズを変更する方法
- reallocを使ったメモリサイズの動的変更とそのタイミング
- 文字列やデータ構造の動的管理の具体例
- 大規模データを効率的に処理するための動的メモリ管理の応用例
mallocを使った可変長配列の実装
C言語では、配列のサイズをコンパイル時に決定する必要があります。
しかし、実行時にサイズを変更したい場合や、サイズが不明な場合には、malloc
を使ってメモリを動的に確保することで可変長配列を実装することができます。
このセクションでは、malloc
を使った可変長配列の基本的な実装方法について解説します。
メモリの動的確保
malloc関数
は、指定したバイト数のメモリをヒープ領域から動的に確保します。
以下に、malloc
を使って整数型の可変長配列を作成する例を示します。
#include <stdio.h>
#include <stdlib.h>
int main() {
int *array;
int size;
// 配列のサイズをユーザーから入力
printf("配列のサイズを入力してください: ");
scanf("%d", &size);
// メモリを動的に確保
array = (int *)malloc(size * sizeof(int));
if (array == NULL) {
printf("メモリの確保に失敗しました。\n");
return 1;
}
// 配列に値を代入
for (int i = 0; i < size; i++) {
array[i] = i + 1;
}
// 配列の内容を表示
for (int i = 0; i < size; i++) {
printf("%d ", array[i]);
}
printf("\n");
// メモリの解放
free(array);
return 0;
}
このプログラムでは、ユーザーから配列のサイズを入力してもらい、そのサイズ分のメモリをmalloc
で確保しています。
確保したメモリに対して、配列のようにアクセスして値を代入し、表示しています。
配列のサイズ変更
動的に確保した配列のサイズを変更するには、realloc関数
を使用します。
realloc
は、既存のメモリブロックを新しいサイズに変更し、必要に応じて新しいメモリブロックを確保します。
#include <stdio.h>
#include <stdlib.h>
int main() {
int *array;
int size = 5;
// 初期サイズでメモリを確保
array = (int *)malloc(size * sizeof(int));
if (array == NULL) {
printf("メモリの確保に失敗しました。\n");
return 1;
}
// 配列に初期値を代入
for (int i = 0; i < size; i++) {
array[i] = i + 1;
}
// 配列のサイズを変更
size = 10;
array = (int *)realloc(array, size * sizeof(int));
if (array == NULL) {
printf("メモリの再確保に失敗しました。\n");
return 1;
}
// 新しい要素に値を代入
for (int i = 5; i < size; i++) {
array[i] = i + 1;
}
// 配列の内容を表示
for (int i = 0; i < size; i++) {
printf("%d ", array[i]);
}
printf("\n");
// メモリの解放
free(array);
return 0;
}
この例では、最初に5つの要素を持つ配列を作成し、その後realloc
を使って10要素に拡張しています。
realloc
を使用することで、既存のデータを保持しつつ、配列のサイズを変更できます。
メモリの解放
動的に確保したメモリは、使用が終わったら必ずfree関数
を使って解放する必要があります。
解放しないと、メモリリークが発生し、プログラムが終了するまでメモリが解放されません。
malloc
やrealloc
で確保したメモリは、free
で解放する。- 解放後のポインタは、再利用を防ぐために
NULL
に設定することが推奨される。
free(array);
array = NULL;
このようにすることで、誤って解放済みのメモリにアクセスすることを防ぎます。
実装の具体例
ここでは、malloc
を使った可変長配列の具体的な実装例を紹介します。
基本的な可変長配列の作成から、要素の追加・削除、そして配列のサイズを動的に変更する方法について解説します。
基本的な可変長配列の例
まずは、基本的な可変長配列の実装例を示します。
この例では、ユーザーが指定したサイズの配列を動的に確保し、初期化して表示します。
#include <stdio.h>
#include <stdlib.h>
int main() {
int *array;
int size;
// 配列のサイズをユーザーから入力
printf("配列のサイズを入力してください: ");
scanf("%d", &size);
// メモリを動的に確保
array = (int *)malloc(size * sizeof(int));
if (array == NULL) {
printf("メモリの確保に失敗しました。\n");
return 1;
}
// 配列に値を代入
for (int i = 0; i < size; i++) {
array[i] = i + 1;
}
// 配列の内容を表示
for (int i = 0; i < size; i++) {
printf("%d ", array[i]);
}
printf("\n");
// メモリの解放
free(array);
return 0;
}
このプログラムでは、ユーザーが入力したサイズに基づいてメモリを確保し、配列を初期化して表示しています。
要素の追加と削除
可変長配列に要素を追加するには、realloc
を使って配列のサイズを増やし、新しい要素を追加します。
削除する場合は、要素を詰めてからサイズを縮小します。
#include <stdio.h>
#include <stdlib.h>
int main() {
int *array;
int size = 5;
// 初期サイズでメモリを確保
array = (int *)malloc(size * sizeof(int));
if (array == NULL) {
printf("メモリの確保に失敗しました。\n");
return 1;
}
// 配列に初期値を代入
for (int i = 0; i < size; i++) {
array[i] = i + 1;
}
// 要素を追加
size++;
array = (int *)realloc(array, size * sizeof(int));
array[size - 1] = 99; // 新しい要素を追加
// 要素を削除(例:2番目の要素を削除)
for (int i = 1; i < size - 1; i++) {
array[i] = array[i + 1];
}
size--;
array = (int *)realloc(array, size * sizeof(int));
// 配列の内容を表示
for (int i = 0; i < size; i++) {
printf("%d ", array[i]);
}
printf("\n");
// メモリの解放
free(array);
return 0;
}
この例では、配列の末尾に新しい要素を追加し、2番目の要素を削除しています。
realloc
を使ってサイズを調整することで、要素の追加と削除を実現しています。
配列のサイズを動的に変更する方法
配列のサイズを動的に変更するには、realloc
を使用します。
realloc
は、既存のメモリブロックを新しいサイズに変更し、必要に応じて新しいメモリブロックを確保します。
#include <stdio.h>
#include <stdlib.h>
int main() {
int *array;
int size = 5;
// 初期サイズでメモリを確保
array = (int *)malloc(size * sizeof(int));
if (array == NULL) {
printf("メモリの確保に失敗しました。\n");
return 1;
}
// 配列に初期値を代入
for (int i = 0; i < size; i++) {
array[i] = i + 1;
}
// 配列のサイズを変更
size = 10;
array = (int *)realloc(array, size * sizeof(int));
if (array == NULL) {
printf("メモリの再確保に失敗しました。\n");
return 1;
}
// 新しい要素に値を代入
for (int i = 5; i < size; i++) {
array[i] = i + 1;
}
// 配列の内容を表示
for (int i = 0; i < size; i++) {
printf("%d ", array[i]);
}
printf("\n");
// メモリの解放
free(array);
return 0;
}
このプログラムでは、最初に5つの要素を持つ配列を作成し、その後realloc
を使って10要素に拡張しています。
realloc
を使用することで、既存のデータを保持しつつ、配列のサイズを変更できます。
mallocを使った可変長配列の応用
malloc
を使った可変長配列は、さまざまな場面で応用が可能です。
ここでは、文字列の動的管理、データ構造の動的生成、大規模データの処理といった具体的な応用例を紹介します。
文字列の動的管理
C言語では、文字列は文字の配列として扱われます。
malloc
を使うことで、動的に文字列のサイズを変更することができます。
以下に、ユーザーから入力された文字列を動的に管理する例を示します。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *str;
int size = 10;
// 初期サイズでメモリを確保
str = (char *)malloc(size * sizeof(char));
if (str == NULL) {
printf("メモリの確保に失敗しました。\n");
return 1;
}
printf("文字列を入力してください: ");
fgets(str, size, stdin);
// 文字列の長さを取得
int len = strlen(str);
// 必要に応じてメモリを再確保
if (len == size - 1 && str[len - 1] != '\n') {
size *= 2;
str = (char *)realloc(str, size * sizeof(char));
printf("追加の文字列を入力してください: ");
fgets(str + len, size - len, stdin);
}
printf("入力された文字列: %s", str);
// メモリの解放
free(str);
return 0;
}
このプログラムでは、初期サイズでメモリを確保し、必要に応じてrealloc
を使ってメモリを拡張しています。
これにより、長い文字列を動的に管理することができます。
データ構造の動的生成
malloc
を使うことで、リンクリストやツリーなどのデータ構造を動的に生成することができます。
以下に、単方向リンクリストのノードを動的に生成する例を示します。
#include <stdio.h>
#include <stdlib.h>
// ノードの定義
typedef struct Node {
int data;
struct Node *next;
} Node;
// 新しいノードを作成
Node* createNode(int data) {
Node *newNode = (Node *)malloc(sizeof(Node));
if (newNode == NULL) {
printf("メモリの確保に失敗しました。\n");
exit(1);
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}
int main() {
Node *head = createNode(1);
head->next = createNode(2);
head->next->next = createNode(3);
// リストの内容を表示
Node *current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
// メモリの解放
current = head;
while (current != NULL) {
Node *next = current->next;
free(current);
current = next;
}
return 0;
}
この例では、malloc
を使ってリンクリストのノードを動的に生成しています。
ノードを追加するたびにメモリを確保し、リストの終端に追加しています。
大規模データの処理
大規模なデータを扱う場合、malloc
を使って必要なメモリを動的に確保することで、効率的にデータを処理することができます。
以下に、動的に確保したメモリを使って大規模な整数配列を処理する例を示します。
#include <stdio.h>
#include <stdlib.h>
int main() {
int *largeArray;
int size = 1000000; // 100万要素の配列
// メモリを動的に確保
largeArray = (int *)malloc(size * sizeof(int));
if (largeArray == NULL) {
printf("メモリの確保に失敗しました。\n");
return 1;
}
// 配列に値を代入
for (int i = 0; i < size; i++) {
largeArray[i] = i;
}
// 配列の一部を表示
for (int i = 0; i < 10; i++) {
printf("%d ", largeArray[i]);
}
printf("...\n");
// メモリの解放
free(largeArray);
return 0;
}
このプログラムでは、100万要素の整数配列を動的に確保し、初期化しています。
malloc
を使うことで、大規模なデータを効率的に処理することが可能です。
よくある質問
まとめ
動的メモリ管理は、C言語プログラミングにおいて重要な技術です。
malloc
やrealloc
を使うことで、実行時にメモリを柔軟に管理し、可変長配列や動的データ構造を実装できます。
この記事を通じて、動的メモリ管理の基本と応用を理解し、実際のプログラムに活用してみてください。