[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関数を使って解放する必要があります。

解放しないと、メモリリークが発生し、プログラムが終了するまでメモリが解放されません。

  • mallocreallocで確保したメモリは、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を使うことで、大規模なデータを効率的に処理することが可能です。

よくある質問

mallocで確保したメモリが足りなくなったらどうする?

mallocで確保したメモリが足りなくなった場合は、reallocを使用してメモリブロックを拡張することができます。

reallocは、既存のメモリブロックを新しいサイズに変更し、必要に応じて新しいメモリブロックを確保します。

例:array = (int *)realloc(array, newSize * sizeof(int));

これにより、データを保持しつつ、メモリを拡張できます。

freeを忘れるとどうなる?

freeを忘れると、メモリリークが発生します。

メモリリークとは、プログラムが終了するまでメモリが解放されず、使用可能なメモリが減少することを指します。

これにより、長時間実行されるプログラムやメモリを多く消費するプログラムでは、システムのメモリが不足し、パフォーマンスが低下する可能性があります。

freeを使用して、不要になったメモリを適切に解放することが重要です。

reallocを使うべきタイミングは?

reallocは、動的に確保したメモリのサイズを変更したいときに使用します。

具体的には、配列の要素数を増やしたり減らしたりする必要がある場合や、文字列の長さを変更する必要がある場合に適しています。

reallocを使用することで、既存のデータを保持しつつ、メモリのサイズを柔軟に変更できます。

ただし、reallocが失敗した場合に備えて、元のポインタを保持しておくことが推奨されます。

まとめ

動的メモリ管理は、C言語プログラミングにおいて重要な技術です。

mallocreallocを使うことで、実行時にメモリを柔軟に管理し、可変長配列や動的データ構造を実装できます。

この記事を通じて、動的メモリ管理の基本と応用を理解し、実際のプログラムに活用してみてください。

当サイトはリンクフリーです。出典元を明記していただければ、ご自由に引用していただいて構いません。

関連カテゴリーから探す

  • URLをコピーしました!
目次から探す