[C言語] 2次元配列をmallocを使ってメモリを動的確保する方法

C言語で2次元配列を動的に確保するには、malloc関数を使用します。

まず、配列の行数分のポインタを格納するためのメモリをmallocで確保します。

次に、各行に対して列数分のメモリをmallocで確保し、これを行ポインタに割り当てます。

この方法により、配列のサイズを実行時に決定でき、メモリの効率的な使用が可能になります。

確保したメモリは使用後にfree関数で解放する必要があります。

この記事でわかること
  • mallocを使った2次元配列の動的メモリ確保の手順
  • 2次元配列の要素へのアクセス方法と初期化方法
  • 動的に確保した配列のサイズ変更方法
  • 行列計算や文字列管理などの応用例
  • メモリリークを防ぐ方法とmallocとcallocの違い

目次から探す

mallocを使った2次元配列の動的確保

C言語では、動的にメモリを確保するためにmalloc関数を使用します。

2次元配列を動的に確保することで、プログラムの柔軟性を高めることができます。

ここでは、mallocを使った2次元配列の動的確保について詳しく解説します。

mallocによるメモリ確保の手順

malloc関数を使ってメモリを確保する手順は以下の通りです。

  1. 必要なメモリサイズを計算する。
  2. malloc関数を呼び出してメモリを確保する。
  3. 確保したメモリをポインタで管理する。

以下は、mallocを使って1次元配列を確保する例です。

#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;
        printf("%d ", array[i]);
    }
    // メモリを解放
    free(array);
    return 0;
}

この例では、5つの整数を格納するためのメモリを動的に確保し、使用後に解放しています。

ポインタ配列を使った2次元配列の確保

ポインタ配列を使って2次元配列を確保する方法は、各行を個別に確保する方法です。

以下にその例を示します。

#include <stdio.h>
#include <stdlib.h>
int main() {
    int rows = 3;
    int cols = 4;
    int **array;
    // 行のポインタ配列を確保
    array = (int **)malloc(rows * sizeof(int *));
    // 各行のメモリを確保
    for (int i = 0; i < rows; i++) {
        array[i] = (int *)malloc(cols * sizeof(int));
    }
    // 配列を使用
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            array[i][j] = i * cols + j;
            printf("%d ", array[i][j]);
        }
        printf("\n");
    }
    // メモリを解放
    for (int i = 0; i < rows; i++) {
        free(array[i]);
    }
    free(array);
    return 0;
}

この方法では、各行のメモリを個別に確保するため、行ごとに異なるサイズの配列を作成することも可能です。

連続メモリブロックを使った2次元配列の確保

連続したメモリブロックを使って2次元配列を確保する方法もあります。

この方法では、1次元配列としてメモリを確保し、2次元配列としてアクセスします。

#include <stdio.h>
#include <stdlib.h>
int main() {
    int rows = 3;
    int cols = 4;
    int *array;
    // 連続したメモリを確保
    array = (int *)malloc(rows * cols * sizeof(int));
    // 配列を使用
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            array[i * cols + j] = i * cols + j;
            printf("%d ", array[i * cols + j]);
        }
        printf("\n");
    }
    // メモリを解放
    free(array);
    return 0;
}

この方法では、メモリが連続しているため、キャッシュ効率が良くなる場合があります。

メモリ解放の方法

動的に確保したメモリは、使用後に必ず解放する必要があります。

解放しないとメモリリークが発生し、プログラムのメモリ使用量が増加し続けます。

  • free関数を使って、確保したメモリを解放します。
  • ポインタ配列を使った場合は、各行のメモリを解放した後、行のポインタ配列自体を解放します。

例:free(array);

mallocを使った2次元配列の操作

動的に確保した2次元配列を操作する方法について解説します。

ここでは、要素へのアクセス、配列の初期化、配列のサイズ変更について説明します。

要素へのアクセス方法

動的に確保した2次元配列の要素にアクセスする方法は、確保方法によって異なります。

以下に、ポインタ配列を使った場合と連続メモリブロックを使った場合のアクセス方法を示します。

ポインタ配列を使った場合

ポインタ配列を使った場合、通常の2次元配列と同様に、array[i][j]の形式で要素にアクセスできます。

#include <stdio.h>
#include <stdlib.h>
int main() {
    int rows = 3;
    int cols = 4;
    int **array;
    // メモリを確保
    array = (int **)malloc(rows * sizeof(int *));
    for (int i = 0; i < rows; i++) {
        array[i] = (int *)malloc(cols * sizeof(int));
    }
    // 要素にアクセス
    array[1][2] = 5;
    printf("array[1][2] = %d\n", array[1][2]);
    // メモリを解放
    for (int i = 0; i < rows; i++) {
        free(array[i]);
    }
    free(array);
    return 0;
}

連続メモリブロックを使った場合

連続メモリブロックを使った場合、array[i * cols + j]の形式で要素にアクセスします。

#include <stdio.h>
#include <stdlib.h>
int main() {
    int rows = 3;
    int cols = 4;
    int *array;
    // メモリを確保
    array = (int *)malloc(rows * cols * sizeof(int));
    // 要素にアクセス
    array[1 * cols + 2] = 5;
    printf("array[1][2] = %d\n", array[1 * cols + 2]);
    // メモリを解放
    free(array);
    return 0;
}

配列の初期化

動的に確保した配列を初期化するには、ループを使って各要素に値を設定します。

以下に、配列を0で初期化する例を示します。

#include <stdio.h>
#include <stdlib.h>
int main() {
    int rows = 3;
    int cols = 4;
    int **array;
    // メモリを確保
    array = (int **)malloc(rows * sizeof(int *));
    for (int i = 0; i < rows; i++) {
        array[i] = (int *)malloc(cols * sizeof(int));
    }
    // 配列を初期化
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            array[i][j] = 0;
        }
    }
    // 初期化結果を表示
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", array[i][j]);
        }
        printf("\n");
    }
    // メモリを解放
    for (int i = 0; i < rows; i++) {
        free(array[i]);
    }
    free(array);
    return 0;
}

配列のサイズ変更

動的に確保した配列のサイズを変更するには、realloc関数を使用します。

ただし、2次元配列の場合、各行のサイズを変更する必要があるため、少し複雑です。

以下に、行数を増やす例を示します。

#include <stdio.h>
#include <stdlib.h>
int main() {
    int rows = 3;
    int cols = 4;
    int newRows = 5;
    int **array;
    // メモリを確保
    array = (int **)malloc(rows * sizeof(int *));
    for (int i = 0; i < rows; i++) {
        array[i] = (int *)malloc(cols * sizeof(int));
    }
    // 行数を増やす
    array = (int **)realloc(array, newRows * sizeof(int *));
    for (int i = rows; i < newRows; i++) {
        array[i] = (int *)malloc(cols * sizeof(int));
    }
    // 新しい行を初期化
    for (int i = rows; i < newRows; i++) {
        for (int j = 0; j < cols; j++) {
            array[i][j] = 0;
        }
    }
    // 結果を表示
    for (int i = 0; i < newRows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", array[i][j]);
        }
        printf("\n");
    }
    // メモリを解放
    for (int i = 0; i < newRows; i++) {
        free(array[i]);
    }
    free(array);
    return 0;
}

この例では、reallocを使って行数を増やし、新しい行を初期化しています。

サイズ変更後は、必ず新しいメモリ領域を初期化することを忘れないでください。

応用例

動的に確保した2次元配列は、さまざまな応用が可能です。

ここでは、行列計算、文字列の管理、画像データの管理といった具体的な応用例を紹介します。

2次元配列を使った行列計算

2次元配列は、行列計算に非常に適しています。

以下に、2つの行列の加算を行う例を示します。

#include <stdio.h>
#include <stdlib.h>
void addMatrices(int **a, int **b, int **result, int rows, int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            result[i][j] = a[i][j] + b[i][j];
        }
    }
}
int main() {
    int rows = 2;
    int cols = 2;
    int **matrixA, **matrixB, **result;
    // メモリを確保
    matrixA = (int **)malloc(rows * sizeof(int *));
    matrixB = (int **)malloc(rows * sizeof(int *));
    result = (int **)malloc(rows * sizeof(int *));
    for (int i = 0; i < rows; i++) {
        matrixA[i] = (int *)malloc(cols * sizeof(int));
        matrixB[i] = (int *)malloc(cols * sizeof(int));
        result[i] = (int *)malloc(cols * sizeof(int));
    }
    // 行列を初期化
    matrixA[0][0] = 1; matrixA[0][1] = 2;
    matrixA[1][0] = 3; matrixA[1][1] = 4;
    matrixB[0][0] = 5; matrixB[0][1] = 6;
    matrixB[1][0] = 7; matrixB[1][1] = 8;
    // 行列を加算
    addMatrices(matrixA, matrixB, result, rows, cols);
    // 結果を表示
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", result[i][j]);
        }
        printf("\n");
    }
    // メモリを解放
    for (int i = 0; i < rows; i++) {
        free(matrixA[i]);
        free(matrixB[i]);
        free(result[i]);
    }
    free(matrixA);
    free(matrixB);
    free(result);
    return 0;
}

この例では、2つの2×2行列を加算し、結果を表示しています。

文字列の2次元配列管理

文字列を2次元配列で管理することで、複数の文字列を効率的に扱うことができます。

以下に、文字列の配列を動的に確保する例を示します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
    int numStrings = 3;
    int maxLength = 20;
    char **strings;
    // メモリを確保
    strings = (char **)malloc(numStrings * sizeof(char *));
    for (int i = 0; i < numStrings; i++) {
        strings[i] = (char *)malloc(maxLength * sizeof(char));
    }
    // 文字列を設定
    strcpy(strings[0], "こんにちは");
    strcpy(strings[1], "世界");
    strcpy(strings[2], "C言語");
    // 文字列を表示
    for (int i = 0; i < numStrings; i++) {
        printf("%s\n", strings[i]);
    }
    // メモリを解放
    for (int i = 0; i < numStrings; i++) {
        free(strings[i]);
    }
    free(strings);
    return 0;
}

この例では、3つの文字列を動的に確保し、表示しています。

動的配列を使った画像データの管理

画像データは通常、ピクセルごとに色情報を持つ2次元配列として表現されます。

以下に、簡単なグレースケール画像を動的に管理する例を示します。

#include <stdio.h>
#include <stdlib.h>
int main() {
    int width = 3;
    int height = 3;
    int **image;
    // メモリを確保
    image = (int **)malloc(height * sizeof(int *));
    for (int i = 0; i < height; i++) {
        image[i] = (int *)malloc(width * sizeof(int));
    }
    // 画像データを設定
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            image[i][j] = i * width + j; // サンプルデータ
        }
    }
    // 画像データを表示
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            printf("%d ", image[i][j]);
        }
        printf("\n");
    }
    // メモリを解放
    for (int i = 0; i < height; i++) {
        free(image[i]);
    }
    free(image);
    return 0;
}

この例では、3×3のグレースケール画像を動的に確保し、サンプルデータを設定して表示しています。

画像処理の基礎として、動的配列を使った管理は非常に有用です。

よくある質問

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

mallocで確保したメモリが足りなくなった場合、realloc関数を使用してメモリを再確保することができます。

reallocは、既存のメモリブロックを拡張または縮小し、新しいサイズのメモリブロックを返します。

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

ただし、reallocが失敗した場合はNULLを返すため、必ず結果を確認し、必要に応じてエラーハンドリングを行ってください。

2次元配列のメモリリークを防ぐには?

2次元配列のメモリリークを防ぐためには、確保したすべてのメモリを適切に解放することが重要です。

ポインタ配列を使った場合は、各行のメモリをfreeで解放した後、行のポインタ配列自体もfreeで解放します。

例:for (int i = 0; i < rows; i++) { free(array[i]); } free(array);

これにより、すべての動的メモリが解放され、メモリリークを防ぐことができます。

mallocとcallocの違いは何ですか?

malloccallocはどちらもメモリを動的に確保するための関数ですが、いくつかの違いがあります。

mallocは指定されたバイト数のメモリを確保し、初期化は行いません。

一方、callocは指定された要素数と要素サイズに基づいてメモリを確保し、すべてのビットをゼロで初期化します。

例:int *array = (int *)calloc(numElements, sizeof(int));

この初期化の違いにより、callocは初期化が必要な場合に便利です。

まとめ

動的にメモリを確保する方法は、C言語プログラミングにおいて重要なスキルです。

この記事では、mallocを使った2次元配列の動的確保と操作方法、応用例、よくある質問について解説しました。

これらの知識を活用して、より効率的で柔軟なプログラムを作成してみてください。

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

関連カテゴリーから探す

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