C언어 배열과 포인터 포인터의 포인터

    2018-02-07 00:46:50 작성

    포인터의 포인터는 포인터변수를 가리키는 포인터변수 입니다.

    int num = 3;
    int * ptr = #	// 변수 num을 가리키는 포인터변수 ptr
    int ** dptr = &ptr;	// 포인터변수 ptr을 가리키는 포인터변수 dptr

    다음과 같은 참조 관계가 됩니다.

    **dptr은 num을 가리키게 됩니다.

    #include <stdio.h>
    
    int main(void) {
    	int num = 3;
    	int *ptr = &num; // 변수 num의 주소값
    	int **dptr = &ptr; // 포인터변수의 주소값
    	int *ptr2 = NULL;
    
    	printf("%p %p\n", &ptr, dptr);
    	printf("%p %p %p\n", &num, ptr, *dptr);
    	printf("%d %d %d\n", num, *ptr, **dptr);
    	ptr2 = *dptr; // *dptr 은 ptr
    	*ptr2 = 10;
    	printf("%d %d %d\n", num, *ptr2, **dptr);
    	return 0;
    }


    ptr의 주소값, dptr의 값이 같습니다.
    num의 주소값, ptr의 값, dptr이 가리키는 값이 서로 같습니다.
    num의 값은 ptr이 가리키는 값, dptr이 가리키는값이 가리키는값과 같습니다.
    말장난 같아 보이지만 실제 그렇습니다.
    ptr2 = *dptr은 ptr2 = ptr과 동일한 결과를 같습니다.
    이렇게 ** 두개 붙은 포인터를 이중포인터라고 합니다.


    포인터 변수의 call by reference

    다음은 이중포인터를 활용하여 포인터의 가리키는 값을 swap하는 예제입니다.

    #include <stdio.h>
    
    void swap(int **dp1, int **dp2);
    
    int main(void) {
    	int num1 = 10, num2 = 20;
    	int * ptr1 = &num1, *ptr2 = &num2;
    	printf("swap 함수 호출 전 : \n");
    	printf("*ptr1 : %d, *ptr2 : %d\n", *ptr1, *ptr2);
    	printf(" num1 : %d,  num2 : %d\n", num1, num2);
    	swap(&ptr1, &ptr2); // 레퍼런스로 전달하기 때문에 주소값 전달
    	printf("\nswap 함수 호출 후 : \n");
    	printf("*ptr1 : %d, *ptr2 : %d\n", *ptr1, *ptr2);
    	printf(" num1 : %d,  num2 : %d\n", num1, num2);
    	return 0;
    }
    // 포인터의 주소를 받기 때문에 이중포인터
    void swap(int **dp1, int **dp2) {
    	int * temp = *dp1;
    	*dp1 = *dp2;
    	*dp2 = temp;
    }


    결과를 보면 실제 값을 변경하는 것이 아닌. ptr1과 ptr2의 가리키는 주소값을 변경했음을 알수 있습니다.

    이렇듯 포인터는 함수 내에서 함수외부의 변수에 접근할수 있는 방법을 제시해줍니다.


    포인터 배열과 이중포인터

    포인터 배열은 포인터변수들로 이루어진 배열입니다.
    문자열 표현에서 살펴보았습니다.

    #include <stdio.h>
    
    int main(void) {
    	int a = 10, b = 20, c = 30;
    	int * ptrArr[] = { &a, &b, &c };
    	int **dptr = ptrArr;
    
    	printf("%d %d %d\n", *ptrArr[0], *ptrArr[1], *ptrArr[2]);
    	printf("%d %d %d\n", *dptr[0], *dptr[1], *dptr[2]);
    	return 0;
    }


    시각을 달리 해보면 int* 을 int 포인터자료형 이라고 하면 int* ptrArr[] 은 당연은 int*자료형을 값으로 갖는 요소들이 있어야 할 것입니다.
    int **dptr 도 int* 자료형을 가리키는 포인터 변수 *dptr 이렇게 나눠서 볼수 있이지 않을까요?
    이 처럼 포인터배열을 이중포인터로 가리키게 하는것이 타당해 보입니다.


    다중포인터 변수

    이중포인터변수를 가리키는 삼중포인터변수도 있을것이고, 삼중 포인터 변수를 가리키는 사중 포인터변수도 있을 것입니다.
    그 이상의 다중 포인터도 가능합니다.
    하지만 삼중포인터 이상의 포인터 변수가 등장한다면 포인터 변수를 남용하는 것은 아닌지, 잘못된 방식으로 접근 하는 것은 아닌지 확인해 볼 필요가 있습니다.
    다음은 3중 포인터의 간단한 예입니다.

    #include <stdio.h>
    
    int main(void) {
    	int a = 10;
    	int *ptr = &a;
    	int **dptr = &ptr;
    	int ***tptr = &dptr;
    
    	printf("%d, %d, %d, %d\n", a, *ptr, **dptr, ***tptr);
    	return 0;
    }



    연습문제

    1. 사용자로부터 5개의 정수를 입력받아 저장할 수 있는 배열을 선언합니다.
    2. 2개의 포인터 변수를 선언(minPtr과 maxPtr)합니다.
    3. minMax(arr, len, &minPtr, &maxPtr); 형태로 호출되면,
    minPtr은 배열에서 가장 작은 값을 가리키게 하고, maxPtr은 가장 큰값을 가리키게 하는 프로그램을 작성하세요.