본문 바로가기
🎛 Others.../- C

[C 언어] 포인터로 2차원 배열 다루기 (주소 접근 및 원소 값 접근)

by Wonit 2021. 7. 3.

 

2차원 배열의 형태

 

우리가 2차원 배열과 포인터에 대한 상관관계를 파악하고 조작하기 위해서는 2차원 배열이 메모리에 어떻게 저장되는지 알아야 한다.

 

다음과 같은 배열이 존재한다고 가정해보자.

 

int arr[3][2] = {
    {11, 22},
    {33, 44},
    {55, 66},
};

 

그럼 메모리에 다음과 같이 저장될 것이라는 그림을 많이 봐 왔다

 

 

하지만 정확이 말 한다면, 이 그림은 조금 이상하다.

 

2차원 배열이라 우리가 보기 쉽게 구분해놓은 것일 뿐이지 저렇게 2차원으로 저장되지 않는다.

 

메모리는 연속적인 특성 덕분에 1차원으로 보는 것이 더 올바르다

 

그럼 더 올바른 구조를 그려보자면 다음과 같다.

 

 

이렇게 먼저 머리에 메모리 구조를 이해하고 있어야 포인터와 2차원 배열의 상관관계에 대해서 알 수 있다.

 

2차원 배열은 연속적인 메모리에 할당된다고 해서 1차원 배열처럼 일열로 보여줬지만 아래에 나올 그림은 가독성을 위해서 2차원 구조로 메모리에 존재한다고 가정해보자.

2차원 배열의 포인터

 

포인터 변수에 2차원 배열을 할당하기 위해서는 int (*pointer)[행의 수]; 형태로 정의해야 한다.

 

바로 코드로 확인해보자

 

#include <stdio.h>

int main() {

    int arr[3][2] = {
            {11, 22},
            {33, 44},
            {55, 66},
    };

    int (*pointer)[2]; // 행을 2로 하는 2차원 배열 포인터 하나 생성

    pointer = arr;

    return 0;
}

 

여기서 괄호의 역할이 많이 중요하다.

 

만약 괄호를 생략하고 *pointer[2] 라고 하면 이는 포인터 배열을 생성하겠다는 뜻으로, int 형 포인터 2개가 연속적으로 저장되는 배열이 생성되게 된다.

 

1차원 배열로 생각하기

 

2차원 배열을 포인터로 다루기 위해서 우선 1차원 배열이 연속적으로 존재한다고 가정해보자

 

그럼 위의 코드에서는 배열 size 가 2인 배열 3개가 연속적으로 존재한다고 이해할 수 있다.

 

 

그럼 2차원 배열에서도 역시 1차원 배열의 개념을 함께 이용할 수 있다.

 

2차원 배열 포인터로 배열 주소 접근하기

 

결국 포인터를 이용해서 이들 각각의 주소로 접근한다면 다음과 같은 방식으로 접근할 수 있다

 

  • arr[0] : *pointer
    • pointer 변수가 가리키는 주소
  • arr[1] : *(pointer + 1)
    • pointer 변수가 가리키는 주소 + 4 byte
  • arr[2] : *(pointer + 2)
    • pointer 변수가 가리키는 주소 + 8 byte

 

 

그럼 이 특성을 이용해서 배열의 모든 원소의 주소에 접근할 수 있다.

 

 

이렇게 포인터와 정수를 이용해서 byte 를 옮겨 모든 원소에 접근할 수 있게 되는데, 이게 조금 헷갈릴 수 있다.

 

arr[1][1] == *(pointer + 1) + 1 을 좀 더 분석해보자

 

 

이는 pointer 가 가리키는 주소 (arr)에 4 byte 를 더하고 +1 로 인해 또 4byte 를 더한 주소값이 되는 것이다.

 

이를 반증문을 통해서 증명해보자면,

 

#include <assert.h>

int main() {

    int arr[3][2] = {
            {11, 22},
            {33, 44},
            {55, 66},
    };

    int (*pointer)[2] = arr;

    assert(&arr[0][0] == *pointer);
    assert(&arr[0][1] == *pointer + 1);

    assert(&arr[1][0] == *(pointer + 1));
    assert(&arr[1][1] == *(pointer + 1) + 1);

    assert(&arr[2][0] == *(pointer + 2));
    assert(&arr[2][1] == *(pointer + 2) + 1);

    return 0;
}

 

2차원 배열 포인터로 배열 원소의 값 가져오기

 

이제 모든 원소의 주소에 접근할 수 있게 되었다! 그럼 참조 연산을 이용해서 실제 값을 취할 수 있게 된다.

 

#include <stdio.h>

int main() {
    int arr[3][2] = {
            {11, 22},
            {33, 44},
            {55, 66},
    };

    int (*pointer)[2] = arr;

    printf("%d\n", **pointer);
    printf("%d\n", *(*pointer + 1));

    printf("%d\n", *(*(pointer + 1) + 0)); // + 0 은 생략 가능
    printf("%d\n", *(*(pointer + 1) + 1));

    printf("%d\n", *(*(pointer + 2) + 0)); // + 0 은 생략 가능
    printf("%d\n", *(*(pointer + 2) + 1));

    return 0;
}

댓글2