본문 바로가기
Programming/C++

포인터의 포인터

by OKOK 2017. 7. 27.

포인터의 포인터 혹은 더블 포인터라 불리는 포인터는 다음과 같이 선언하게 됩니다. int **dp


더블 포인터가 가리키는 것은 싱글 포인터

포인터는 무조건 메모리 공간의 주소 값을 저장하는 변수입니다. 따라서 모든 포인터 변수는 4바이트 메모리 공간을 요구합니다. 


#include <stdio.h>


void pswap(int *p1, int *p2);


int main(void)

{

int A = 10, B = 20;

int *pA, *pB;


pA = &A, pB = &B;


printf("pA -> value : %d\n", *pA);

printf("pB -> value : %d\n", *pB);


pswap(pA, pB);


printf("pA -> value : %d\n", *pA);

printf("pB -> value : %d\n", *pB);


return 0;

}


void pswap(int *p1, int *p2)

{

int *temp;

temp = p1;

p1 = p2;

p2 = temp;


함수 pswap은 포인터가 가리키는 대상을 변경시키지 못했음을 알 수 있다. 


#include <stdio.h>


void pswap(int *p1, int *p2);


int main(void)

{

int A = 10, B = 20;

int *pA, *pB;


pA = &A, pB = &B;


printf("pA -> value : %d\n", *pA);

printf("pB -> value : %d\n", *pB);


pswap(pA, pB);


printf("pA -> value : %d\n", *pA);

printf("pB -> value : %d\n", *pB);


return 0;

}


void pswap(int *p1, int *p2)

{

int *temp;

temp = p1;

p1 = p2;

p2 = temp;


포인터 pA와 pB의 값을 넘겨주는 것이 아니라, 포인터 pA와 pB의 주소 값을 넘겨주고 있습니다. pA, pB는 싱글 포인터이므로, 이들의 주소 값은 더블 포인터를 통해서 저장되어야 할 것입니다. 따라서 정의되어 있는 함수 pswap은 매개 변수로 더블 포인터를 선언하고 있습니다. 더블 포인터 p1, p2는 pswap 함수 내의 매개 변수로, pswap 내에서 p1 과 p2가 가리키는 포인터의 값을 바꿀 수 있습니다.  지금까지 더블 포인터에 대해서 이야기하였는데, 그 이야기의 초점이 포인터를 이용한 Call By Reference에 맞춰져 있었습니다. 이것을 제대로 이해한다는 것은 더블 포인터를 명확히 이해했다는 뜻입니다. 사실 더블 포인터라고 해서 별 다를 것이 없습니다. 변수이고, 싱글 포인터의 주소 값을 저장할 뿐입니다.


포인터 배열과 포인터 타입

배열 이름의 포인터 타입을 결정짓는 것을 중요합니다. 포인터이 타입을 알아야 배열을 함수의 인자로 전달할 수도 있고, 반환 할 수도 있기 때문입니다. 포인터를 요소로 지니는 배열들입니다. 1차원 배열 이름의 포인터 타입을 결정짓는 것은 쉽습니다. 배열 이름이 가리키는 대상이 무엇인지를 알면 포인터 타입을 결정지을 수 있기 때문입니다. 


다차원 배열 그리고 포인터

함수로 전달되는 배열의 이름 arr 은 int형 포인터 (int *)로 받을 수 있다. int 형 변수를 요소로 지니는 1차원 배열의 이름 역시 int형 포인터이기 때문에 가능한 일입니다. 


#include <stdio.h>


int main(void)

{

int a[3][2] = { 1,2,3,4,5,6 };


printf("a : %d\n", a);

printf("a+1 : %d\n", a + 1);

printf("a+2 : %d\n", a + 2);


return 0;


2차원 배열 이름은 포인터 연산 시 행 단위로 이동을 한다라고 말할 수 있습니다. 

포인터 타입에는 이동에 대한 정보가 들어 있습니다. 포인터의 타입에는 이동에 대한 정보가 들어 있습니다. 따라서 임의의 두 포인터 타입이 일치한다면, 기본적으로 포인터 연산에 의해 증가 및 감소되는 값의 크기가 일치해야 한다고 결론 내릴 수 있습니다. 


#include <stdio.h>


int main(void)

{

int arr1[3][2];

int arr2[2][3];


printf("arr1 : %d\n", arr1);

printf("arr1 + 1 : %d\n", arr1 + 1);

printf("arr1+2 : %d\n", arr1 + 2);


printf("arr2 : %d \n", arr2);

printf("arr2+1:%d\n", arr2 + 1);

printf("arr2+2:%d\n", arr2 + 2);


return 0;


arr1 과 arr2는 배열 요소의 자료형이 같은 2차원 배열임에도 불구하고, 이름 자체가 지니는 포인터 타입은 다르다고 결론 내릴 수 있습니다. 왜냐하면 포인터 연산 시 증가되는 값이 차이를 보이기 때문입니다. 배열 이름 arr1은 int형 포인터(int *)이자, 포인터 연산 시 배열 요소를 2칸씩 건너 뛰는 포인터입니다. 배열 이름 arr2는 int형 포인터 (int *)이자, 포인터 연산 시 배열 요소를 3칸씩 건너 뛰는 포인텅비니다. int형 포인터라고 해서 모두 같은 것이 아닙니다. 포인터 연산 시 증가 및 감소되는 값에 따라서 그 타입은 달라집니다.


int (*pArr)[4] (*pArr)은 pArr이 포인터 변수 선언임을 말하는 것입니다. 그러므로 위의 선언은 배열이 아니라 포인터 변수 선언입니다. int는 가리킬 수 있는 대상에 대한 정보를 주고 있습니다. 즉 int 형 변수를 가리키는 포인터라는 뜻입니다. 포인터 연산 시 증가 및 감소되는 값의 크기에 따라서 포인터의 타입은 나뉘어 집니다. 포인터 선언의 뒤에 있는 [4] 는 포인터 연산에 따른 증가 혹은 감소의 폭을 이야기 합니다. 최종적으로 결론을 내리면 pArr은 int arr1[2][4], int arr2[3][4] 와 같은 배열을 가리킬 수 있는 포인터입니다. 이렇듯 배열을 가리킬 수 있다고 해서 배열 포인터라고 합니다. 


#include <stdio.h>


void show_data(int(*ptr)[4], int a);


int main(void)

{

int arr1[2][4] = { 1,2,3,4,5,6,7,8 };

int arr2[3][4] = { {1},{2},{3} };


show_data(arr1, 2);

show_data(arr2, 3);


return 0;

}


void show_data(int (*ptr)[4], int a)

{

int i, j;

printf(" Start Print \n");

for (i = 0; i < a; i++)

{

for (j = 0; j < 4; j++)

printf("%d ", ptr[i][j]);

printf("\n");

}


선언한 배열 이름 arr1, arr2를 봅니다. 비록 배열의 길이와 이름은 다르지만, 둘 다 int형 1차원 포인터이면서 포인터 연산 시 4칸씩 이동을 합니다. 따라서 이 둘의 포인터 타입은 int (*ptr)[4] 로 동일합니다. show_data 라는 함수 선언 시 int (*ptr)][4]를 대신해서 int ptr[][4]와 같은 선언이 와도 됩니다. 이 둘은 동일합니다. void show_data(int(*ptr)[4], int a); == void show_data(int ptr[][4], int a); int ptr[][4]와 같은 포인터 선언은 함수의 매개 변수 선언시에만 가능한 것으로써 예외적으로 허용이 되는 것이다. 


int (*pArr)[4] 와 int* pArr[4]의 차이점 전자는 배열을 가리키는 포인터이다. 배열 포인터. int형 변수를 요소로 지니고 포인터 연산 시 4칸씩 이동하는 2차원 배열을 가리키는 포인터이다. 반면에 int* pArr[4]는 배열이다. 포인터 배열.int 형 변수의 주소 값 4개를 저장할 수 있는 배열이다. 



2차원 배열에서의 arr[i] 와 *(arr+i)

가장 중요한 내용은 2차원 배열의 포인터타입을 결정짓는 방법에 대한 이해이다. 2차원 배열의 포인터 타입은 배열 이름이 가리키는 요소의 타입과, 포인터 연산 시 이동하는 바이트 수에 따라서 결정된다는 기본 원리를 반드시 기억해야 한다.