본문 바로가기
C언어

C언어 기초 part 2-5. 더블 포인터

by algosketch 2020. 6. 1.
  • 더블 포인터

1. 더블 포인터

 포인터 변수는 메모리 주소값을 저장한다. 포인터 변수를 선언하게 되면 주소값을 저장하기 위해 공간을 새로 할당한다. 당연히 그 공간에 대한 주소값도 존재하게 된다. 이 포인터 변수의 주소값을 저장하는 포인터 변수를 선언하면 그게 바로 더블 포인터가 된다. 눈치 챘겠지만 3차, 4차 이상의 다중 포인터를 선언하는 것도 가능하다.

 함수에서 매개변수로 배열이 존재한다면,

void function1(int arr[]) {
...
}

 

 위와 같은 형태로 존재할 것이다. 그러나 매개변수를 이렇게 선언하는 것은 프로그래머가 읽을 때 배열이라는 형태라고 생각하도록 유도하기 위함이지 실제로는 포인터로 받게 된다. 즉, 위 코드는 다음 코드로 변환된다.

void function1(int * arr) {
...
}

 

 마찬가지로 2차원 배열도 인수를 더블 포인터 형태로 받게 된다. (2차원 배열을 매개변수로 받는 함수를 구현하는 것은 사실 조금 까다롭다.)

 다음 예제 코드로 선언하는 방법을 살펴보자.

int num = 5;
int * ptr = #
int ** dptr = &ptr;
// num == *ptr == **dptr
// &num == ptr, &ptr == dptr

 

 더블 포인터는 **을 사용하여, 3중 포인터는 ***을 사용하여 선언한다. 이제 주석처리한 부분을 보라! 포인터 연산에서 *연산은 값이 나올 때까지 가능하다. 하지만 &&num 가 dptr와 같을 거라 생각한다면 그건 틀린 생각이다. dptr이 존재하려면 포인터 변수인 ptr가 먼저 생성되어야 한다. 어떤 변수의 주소값의 주소값, 논리적으로도 이상하지 않은가?

 

 앞서 배열은 포인터라고 한 적이 있다. 더블 포인터를 이해하기 위해 2차원 배열을 이용하려고 한다. 포인터 연산에서 다음 연산이 어떤 결과를 갖게될까?

int arr[5][3] = {0, };
arr+1; // 연산 1
arr[0]+1;    // 연산 2
printf("%p %p %p", arr, arr+1, arr[0]+1);

 

 arr이 1000번지라면 arr+1과 arr[0]+1 모두 1004번지를 가리키게 될까? 결과는 arr+1은 1012번지를, arr[0]+1은 1004번지를 가리킨다. arr은 더블 포인터, arr[0]은 포인터이다. 둘 모두 주소값은 같지만 arr의 다음 값은 arr[1]인 반면, arr[0]의 다음 값은 &(arr[0][1])이다. 즉, arr+1연산에서, 포인터는 int 자료형의 주소값이 아닌 int [3] 자료형의 주소값으로 인식한다.(C에서 실제로 이런 자료형은 없다. 하지만 어떤 의미로 사용했는지 이해하리라 생각한다.)