38.3 포인터에 할당된 메모리를 2차원 배열처럼 사용하기

이번에는 포인터에 메모리를 할당하여 세로 크기 3, 가로 크기 4인 2차원 배열처럼 사용해보겠습니다.다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.

  • 자료형 **포인터이름 = malloc(sizeof(자료형 *) * 세로크기);와 같이 세로 공간 메모리 할당
    • 반복문으로 반복하면서 포인터[i] = malloc(sizeof(자료형) * 가로크기);와 같이 가로 공간 메모리 할당
    • 반복문으로 반복하면서 free(포인터[i]);와 같이 가로 공간 메모리 해제
    • free(포인터);와 같이 세로 공간 메모리 해제

pointer_like_two_dimensional_array.c

#include <stdio.h>
#include <stdlib.h>    // malloc, free 함수가 선언된 헤더 파일

int main()
{
    int **m = malloc(sizeof(int *) * 3);   // 이중 포인터에 (int 포인터 크기 * 세로 크기)만큼
                                           // 동적 메모리 할당. 배열의 세로

    for (int i = 0; i < 3; i++)            // 세로 크기만큼 반복
    {
        m[i] = malloc(sizeof(int) * 4);    // (int 크기 * 가로 크기)만큼 동적 메모리 할당.
                                           // 배열의 가로
    }

    m[0][0] = 1;    // 세로 인덱스 0, 가로 인덱스 0인 요소에 값 할당
    m[2][0] = 5;    // 세로 인덱스 2, 가로 인덱스 0인 요소에 값 할당
    m[2][3] = 2;    // 세로 인덱스 2, 가로 인덱스 3인 요소에 값 할당

    printf("%d\n", m[0][0]);    // 1: 세로 인덱스 0, 가로 인덱스 0인 요소의 값 출력
    printf("%d\n", m[2][0]);    // 5: 세로 인덱스 2, 가로 인덱스 0인 요소의 값 출력
    printf("%d\n", m[2][3]);    // 2: 세로 인덱스 2, 가로 인덱스 3인 요소의 값 출력

    for (int i = 0; i < 3; i++)    // 세로 크기만큼 반복
    {
        free(m[i]);                // 2차원 배열의 가로 공간 메모리 해제
    }

    free(m);    // 2차원 배열의 세로 공간 메모리 해제

    return 0;
}

실행 결과

1
5
2

먼저 다음과 같이 이중 포인터에 2차원 배열의 세로 공간에 해당하는 메모리를 할당합니다. 이때 세로 공간에는 값이 들어가지 않고 가로 공간의 메모리 주소가 들어갑니다. 따라서 sizeof(int)가 아닌 sizeof(int *)처럼 포인터의 크기를 구한 뒤 세로 크기 3을 곱해줍니다(32비트에서는 int나 int *의 크기가 4바이트라 큰 차이가 없지만 64비트에서는 int의 크기가 4바이트, int *의 크기가 8바이트이므로 포인터가 들어갈 공간이라는 것을 확실하게 구분해야 합니다).

int **m = malloc(sizeof(int *) * 3);    // 이중 포인터에 (int 포인터 크기 * 세로 크기)만큼
                                        // 메모리 할당. 배열의 세로

이중 포인터에 2차원 배열의 세로 공간에 해당하는 메모리를 할당하는 모습을 그림으로 표현하면 다음과 같이 됩니다.

▼ 그림 38‑2 이중 포인터에 배열의 세로 공간 할당

이제 세로 크기만큼 반복하면서 2차원 배열의 가로 공간에 해당하는 메모리를 할당합니다. 가로 공간에는 int형 숫자가 들어갈 것이므로 sizeof(int)에 가로 크기 4를 곱해줍니다.

for (int i = 0; i < 3; i++)            // 세로 크기만큼 반복
{
    m[i] = malloc(sizeof(int) * 4);    // (int 크기 * 가로 크기)만큼 동적 메모리 할당.
                                       // 배열의 가로
}

이중 포인터에 2차원 배열의 가로 공간에 해당하는 메모리를 할당하는 모습을 그림으로 표현하면 다음과 같이 됩니다.

▼ 그림 38‑3 이중 포인터에 배열의 가로 공간 할당

즉, m은 pointer to pointer to int이므로 int **m으로 선언하고 m[0], m[1], m[2]는 pointer to int이므로 int *가 들어갑니다(메모리 할당). 마지막으로 m[0][0], m[0][1] 등은 int만 들어갑니다.

이제 2차원 배열을 사용하듯이 [ ] [ ]에 세로 인덱스, 가로 인덱스를 지정하여 값을 할당하거나 가져올 수 있습니다.

  • 포인터[세로인덱스][가로인덱스]
m[0][0] = 1;    // 세로 인덱스 0, 가로 인덱스 0인 요소에 값 할당
m[2][0] = 5;    // 세로 인덱스 2, 가로 인덱스 0인 요소에 값 할당
m[2][3] = 2;    // 세로 인덱스 2, 가로 인덱스 3인 요소에 값 할당

printf("%d\n", m[0][0]);    // 1: 세로 인덱스 0, 가로 인덱스 0인 요소의 값 출력
printf("%d\n", m[2][0]);    // 5: 세로 인덱스 2, 가로 인덱스 0인 요소의 값 출력
printf("%d\n", m[2][3]);    // 2: 세로 인덱스 2, 가로 인덱스 3인 요소의 값 출력

포인터를 다 사용했다면 먼저 가로 공간에 해당하는 메모리부터 해제합니다. 그러고 나서 세로 공간에 해당하는 메모리를 해제합니다. free(m);처럼 세로 공간에 해당하는 메모리를 먼저 해제해버리면 가로 공간 메모리를 해제할 수 없으므로 주의합니다.

for (int i = 0; i < 3; i++)    // 세로 크기만큼 반복
{
    free(m[i]);                // 2차원 배열의 가로 공간 메모리 해제
}

free(m);    // 2차원 배열의 세로 공간 메모리 해제

즉, 메모리를 할당할 때 세로 → 가로 순서로 할당했으므로 해제할 때는 반대로 가로 → 세로 순서로 해제합니다.