이중 포인터 또는 더블 포인터는 포인터 변수를 가리키는 포인터 변수를 말한다.
포인터 변수도 변수이므로 주소를 가지며, 그 주소 또한 다른 포인터 변수가 저장하는 것이 가능하다. 이 때 포인터 변수의 주소값을 저장하는 포인터 변수가 바로 더블 포인터이다.
더블 포인터는 다음과 같이 선언한다.
int **dptr;
사용법은 싱글 포인터와 크게 다를 바 없다. 예시를 보자.
int main(void){
double num = 3.14; //double형 변수 num
double *ptr = # //double형 싱글 포인터 변수 ptr
double **dptr = &ptr; //double형 이중 포인터 변수 dptr
}
이 상황을 그림으로 나타내면 다음과 같다.
이 때 num에 접근할 수 있는 방법은 여러가지이다.
double equal;
equal = num;
equal = *ptr; //*ptr == num
equal = **dptr; //*dptr == num
**dptr == *(*dptr) == *ptr == num
포인터 배열은 배열의 각 원소가 포인터인 배열이었다. 근데 배열의 이름이 배열의 첫 번째 원소를 가리키는 포인터 변수가 됨을 공부했다.
그렇다면 포인터 배열의 이름은 어떻게 되는 것일까? 마찬가지로 첫 번째 원소를 가리키게 된다. 포인터 변수를 가리키는 포인터이므로 더블 포인터라 할 수 있다.
즉, 포인터 배열의 이름은 더블 포인터이다.
지금까지 변수의 주소를 저장하는 싱글 포인터, 포인터 변수의 주소를 저장하는 더블 포인터에 대해 공부했다. 싱글 포인터 변수의 주소를 저장하는 더블 포인터의 주소를 저장하는 포인터 변수도 있을까? 있다. 삼중 포인터 변수가 된다.
int ***tptr = &dptr;
이 때 다음과 같은 관계에 있다.
***tptr == **(*tptr) == **dptr == *(*dptr) == *ptr == num
다음과 같이 선언된 2차원 배열이 있다.
int arr[3][3];
배열의 이름 arr가 가리키는 것은 배열의 첫 번째 원소, 즉 첫 번째 행의 첫 번째 원소arr[0][0]
이다.
2차원 배열에서 각 행 arr[0], arr[1], arr[2] 또한 의미를 가지는데, 그 행의 첫 번째 요소를 가리킨다.
1차원 배열 이름에 +1을 하면 포인터 변수가 가리키는 변수의 자료형의 크기만큼 증가한다. 2차원 배열 이름에 +1을 하면 어떻게 될까?
한 행의 크기만큼 증가하여 다음 행을 가리키게 된다. 따라서 같은 자료형의 배열이라 하더라도 행의 길이에 따라 포인터 형이 다르다.
2차원 배열 int arr[3][3]의 포인터 변수는 int (*ptr) [3];
와 같이, int arr[3][4]의 포인터 변수는 int (*ptr) [4];
와 같이 선언하면 된다.
포인터 변수 선언 시 적어야 할 것은 '열'이다. 한 행의 길이라고 생각해도 된다.
❗전에 공부했던 포인터 배열과 2차원 배열의 포인터의 선언은 비슷하므로 주의❗
int *arr[10]; //10개의 int형 포인터 변수를 저장할 수 있는 배열(8X10 = 80바이트)
int (*arr) [10]; //10개의 열을 가진 2차원 배열의 포인터(8바이트)
함수의 인자로 2차원 배열을 넘길 때 역시 값에 의한 호출이 아닌 참조에 의한 호출로, 주소값을 넘겨줘야하므로 포인터를 매개변수로 선언해야 한다.
이 때, 다음 두 표현을 사용할 수 있다.
int (*arr) [10]
int arr[][10]
두 번째 표현은 매개변수 선언 시에만 사용할 수 있다.
다음의 2차원 배열이 있을 때,
int arr[3][2];
아래의 표현은 모두 동일하다.
arr[2][1] = 5;
(*(arr + 2))[1] = 5;
*(arr[2] + 1) = 5;
*(*(arr + 2) + 1) = 5;