34.6 2차원 배열을 포인터에 넣기

1차원 배열은 단일 포인터에 넣을 수 있었습니다. 그러면 2차원 배열은 이중 포인터에 넣을 수 있지 않을까요?

int numArr[3][4] = {    // 세로 크기 3, 가로 크기 4인 int형 2차원 배열 선언
    { 11, 22, 33, 44 },
    { 55, 66, 77, 88 },
    { 99, 110, 121, 132 }
};

int **numPtr = numArr;    // 자료형이 다르다는 경고 발생

printf("%d\n", numPtr[0][0]);    // 실행 에러

하지만 컴파일을 해보면 다음과 같이 자료형이 다르다는 경고가 발생합니다.

array.c(11): warning C4047: '초기화 중': 'int **'의 간접 참조 수준이 'int (*)[4]'과(와) 다릅니다.

실행을 해보면 에러가 발생하여 numPtr[0][0]의 값은 출력되지 않습니다. 2차원 배열을 포인터에 담으려면 다음과 같이 특별한 방법이 필요합니다.

  • 자료형 (*포인터이름)[가로크기];

즉, 포인터를 선언할 때 *과 포인터 이름을 괄호로 묶어준 뒤 [ ]에 가로 크기를 지정합니다.

int (*numPtr)[4];

풀어서 설명하면 가로 크기가 4인 배열을 가리키는 포인터라는 뜻입니다.

그림 34‑3 2차원 배열과 포인터
참고 | int *numPtr[4]

int (*numPtr)[4]에서 괄호를 뺀 int *numPtr[4]int형 포인터 4개를 담을 수 있는 배열이라는 뜻입니다. 즉, 괄호가 있으면 배열을 가리키는 배열 포인터, 괄호가 없으면 포인터를 여러 개 담는 포인터 배열입니다.

int num1, num2, num3, num4;
int *numPtr[4] = { &num1, &num2, &num3, &num4 };    // int형 포인터를 4개 담는 배열

이제 2차원 배열을 포인터에 할당해서 사용해보겠습니다.

pointer_to_two_dimensional_array.c

#include <stdio.h>

int main()
{
    int numArr[3][4] = {    // 세로 3, 가로 4 크기의 int형 2차원 배열 선언
        { 11, 22, 33, 44 },
        { 55, 66, 77, 88 },
        { 99, 110, 121, 132 }
    };

    int (*numPtr)[4] = numArr;

    printf("%p\n", *numPtr); // 002BFE5C: 2차원 배열 포인터를 역참조하면 세로 첫 번째의 주소가 나옴
                             // 컴퓨터마다, 실행할 때마다 달라짐

    printf("%p\n", *numArr); // 002BFE5C: 2차원 배열을 역참조하면 세로 첫 번째의 주소가 나옴
                             // 컴퓨터마다, 실행할 때마다 달라짐

    printf("%d\n", numPtr[2][1]);    // 110: 2차원 배열 포인터는 인덱스로 접근할 수 있음

    printf("%d\n", sizeof(numArr));  // 48: sizeof로 2차원 배열의 크기를 구하면 배열이 메모리에 
                                     // 차지하는 공간이 출력됨

    printf("%d\n", sizeof(numPtr));  // 4 : sizeof로 2차원 배열 포인터의 크기를 
                                     // 구하면 포인터의 크기가 출력됨(64비트라면 8)

    return 0;
}

실행 결과

002BFE5C
002BFE5C
110
48
4

int (*numPtr)[4] = numArr;와 같이 2차원 배열을 포인터에 바로 할당할 수 있습니다. 단, 자료형과 가로 크기가 일치해야 합니다.

2차원 배열을 포인터에 할당한 뒤 포인터를 역참조해보면 배열의 세로 첫 번째 주솟값이 나옵니다. 즉, 배열이 시작하는 주소입니다. 마찬가지로 2차원 배열 자체도 역참조해보면 배열의 세로 첫 번째 주솟값이 나옵니다.

printf("%p\n", *numPtr); // 002BFE5C: 2차원 배열 포인터를 역참조하면 세로 첫 번째의 주소가 나옴
                         // 컴퓨터마다, 실행할 때마다 달라짐

printf("%p\n", *numArr); // 002BFE5C: 2차원 배열을 역참조하면 세로 첫 번째의 주소가 나옴
                         // 컴퓨터마다, 실행할 때마다 달라짐

2차원 배열 포인터는 [ ]를 두 번 사용하여 배열의 요소에 접근할 수 있습니다.

printf("%d\n", numPtr[2][1]);    // 110: 2차원 배열 포인터는 인덱스로 접근할 수 있음

배열과 포인터가 다른 점은 sizeof로 크기를 계산했을 때입니다. sizeof로 배열의 크기를 구해보면 배열이 메모리에 차지하는 공간이 출력되지만 sizeof로 배열의 주소가 들어있는 포인터의 크기를 구해보면 그냥 포인터의 크기만 나옵니다(32비트라면 4, 64비트라면 8).

printf("%d\n", sizeof(numArr));   // 48: sizeof로 2차원 배열의 크기를 구하면 배열이 메모리에 
                                  // 차지하는 공간이 출력됨

printf("%d\n", sizeof(numPtr));   // 4 : sizeof로 2차원 배열 포인터의 크기를 
                                  // 구하면 포인터의 크기가 출력됨(64비트라면 8)
참고 | 3차원 배열

3차원 배열은 다음과 같이 높이x가로x세로 형태로 이루어져 있습니다.

그림 34‑4 3차원 배열

3차원 배열은 [ ]에 높이, 세로 크기, 가로 크기를 지정하여 선언합니다. 값을 초기화할 때는 면 단위로 중괄호를 묶어주면 편리합니다.

  • 자료형 배열이름[높이][세로크기][가로크기];
자료형 배열이름[높이][세로크기][가로크기] = { 
    {
        { 값, 값, 값 },
        { 값, 값, 값 }
    }, 
    {
        { 값, 값, 값 },
        { 값, 값, 값 }
    }
}

다음은 3차원 배열 예제입니다.

int numArr[2][3][4] = {
    {
        { 11, 22, 33, 44 },
        { 55, 66, 77, 88 },
        { 99, 110, 121, 132 }
    },
    {
        { 111, 122, 133, 144 },
        { 155, 166, 177, 188 },
        { 199, 1110, 1121, 1132 }
    }
};

3차원 배열에 접근하려면 [ ]를 세 번 사용하여 높이, 세로, 가로 인덱스를 지정해주면 됩니다.

  • 배열[높이인덱스][세로인덱스][가로인덱스];
  • 배열[높이인덱스][세로인덱스][가로인덱스] = 값;
printf("%d\n", numArr[0][2][1]);    // 110
numArr[1][1][2] = 0;    // 요소에 값 저장

3차원 배열의 높이(깊이), 세로, 가로를 구하는 방법은 다음과 같습니다(보통 높이는 깊이를 뜻하는 depth로 표현).

int depth = sizeof(numArr) / sizeof(numArr[0]);     // 2: 3차원 배열이 차지하는 전체 공간을 
                                                    // 면의 크기로 나눠줌
int row = sizeof(numArr[0]) / sizeof(numArr[0][0]); // 3: 한 면의 크기를 가로 한 줄의 크기로 나눠줌
int column = sizeof(numArr[0][0]) / sizeof(int);    // 4: 가로 한 줄의 크기를 요소의 크기로 나눠줌

3차원 배열을 포인터에 할당하려면 세로x가로로 구성된 면을 가리키는 포인터를 선언하면 됩니다.

  • 자료형 (*포인터이름)[세로크기][가로크기]
int (*numPtr)[3][4] = numArr;    // 세로 크기 3, 가로 크기 4인 면을 가리키는 포인터 선언

지금까지 2차원 배열과 3차원 배열에 대해 배웠는데 문법이 좀 복잡했습니다. 다차원 배열은 어려운 주제이므로 당장 이해가 되지 않는다면 그냥 넘어가도 됩니다. 일단 2차원 배열은 세로, 가로 순으로 선언 및 접근한다는 점만 기억하면 됩니다(3차원 배열은 높이, 세로, 가로 순).