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

    2018-05-09 10:28:55 작성

    배열의 포인터

    1차원 배열을 함수의 인자로 받는다면 다음과 같이 선언될 수 있습니다.

    int arr[10];
    func(arr);
    void func(int *arr) { ... }

    함수를 다음과 같이 선언해도 됩니다.

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

    이것은 이전에 설명 드린바 있습니다.
    2차원 배열을 함수로 넘긴다면 어떨까요?
    '1차원배열을 포인터 변수로 받았으니 2차원 배열은 이중포인터 변수로 받으면 되겠구나!'라고 생각할 수 있습니다.
    다음과 같은 형태가 되겠네요.

    int arr[3][3];
    func(arr);
    void func(int **arr) { ... } // 이것은 잘못된 호출입니다.

    2차원 배열 arr은 int** 자료형이 아닙니다.

    arr이 실제 가리키는 것은 arr[0] 입니다. arr의 자료형은 arr 배열의 하나의 행이라고 볼수 있습니다.

    #include <stdio.h>
    
    int main(void) {
        int arr[3][3];
    
    	printf("arr       주소 : %p  |  길이 : %d\n", arr, sizeof(arr));
    	printf("arr[0]    주소 : %p  |  길이 : %d\n", arr[0], sizeof(arr[0]));
    	printf("arr[1]    주소 : %p  |  길이 : %d\n", arr[1], sizeof(arr[1]));
    	printf("arr[2]    주소 : %p  |  길이 : %d\n", arr[2], sizeof(arr[2]));
    	return 0;
    }


    arr은 배열을 가리키는 포인터입니다.


    배열이름 기반의 포인터 연산

    포인터는 증감연산을 할 수 있습니다. 2차원 배열에서 포인터의 이름을 기반으로 증감연산을 한다면 다음과 같습니다.

    #include <stdio.h>
    
    int main(void) {
    	int arr[3][3];
    
    	printf("arr       주소 : %p\n", arr);
    	printf("arr+1     주소 : %p\n", arr + 1);
    	printf("arr+2     주소 : %p\n", arr + 2);
    	return 0;
    }


    arr + 1의 결과는 arr[0]의 사이즈인 12 만큼 이동했습니다.
    arr + 2의 결과는 arr[0]의 사이즈인 24 만큼 이동했습니다.
    이처럼 포인터는 증감 연산을 했을때 자료형의 크기만큼 이동해야 합니다.

    배열포인터는 다음과 같은 정보를 가지고 있어야 합니다.
    • 가리키는 대상이 무엇인가?
    • 배열포인터를 대상으로 증감연산시 실제로 얼마만큼 증감이 되는가?

    정리를 해보면 다음과 같은 배열이 있을때

    int arr[3][3];
    • arr이 가리키는 대상은 int형 배열
    • arr의 값을 1 증가 시키면 실제 12(sizeof(int) * 3열)만큼 주소값이 증가

    위와 같은 형태의 포인터는 다음과 같이 선언할 수 있습니다.

    int (*ptr)[3]; // 배열 포인터 선언

    다음은 포인터배열 선언입니다 해깔리면 안되요!!

    int * ptr[3]; // 포인터 배열 선언



    2차원 배열을 함수의 인자로 전달하기

    다음과 같은 형식이 되어야 합니다.

    int arr[3][4];
    func(arr);
    
    func(int (*parr)[4]) { ... } // 이렇게 하거나
    func(int parr[][4]) { ... } // 또는 이렇게 합니다.

    (*parr)[4]parr[][4]는 동일한 표현입니다.
    다음은 2차원 배열을 받아 배열요소를 출력합니다.

    #include <stdio.h>
    #define COL 4
    
    void showArr(int(*arr)[COL], const int ROW);
    
    void main(void) {
    	int arr[][COL] = {
    		{1,2,3,4},
    		{5,6,7,8},
    		{9,10,11,12},
    	};
    	int len = sizeof(arr) / sizeof(arr[1]);
    
    	showArr(arr, len);
    	return;
    }
    
    void showArr(int (*arr)[COL], const int ROW) {
    	int i, j;
    
    	for (i = 0; i < ROW; i++) {
    		for (j = 0; j < COL; j++) {
    			printf("%2d ", arr[i][j]);
    		}
    		printf("\n");
    	}
    }
    



    2차원배열에서도 배열식과 포인터식은 같습니다.

    배열과 포인터에서 다음은 같은 의미입니다.

    arr[i] == *(arr+i) // 같다

    2차원배열에서의 포인터식은 다음과 같습니다.

    arr[i][j] == *(arr[i]+j) == (*(arr+i))[j] == *(*(arr+i)+j) // 같다

    다음 코드에서 확인해보겠습니다.

    #include <stdio.h>
    
    int main(void) {
    	int arr[][4] = { { 1,2,3,4 },{ 5,6,7,8 } };
    	int i = 1, j = 2;
    
    	printf(" arr[i][j]     : %d\n", arr[i][j]);
    	printf(" *(arr[i]+j)   : %d\n", *(arr[i] + j));
    	printf(" (*(arr+i))[j] : %d\n", (*(arr + i))[j]);
    	printf(" *(*(arr+i)+j) : %d\n", *(*(arr + i) + j));
    	return 0;
    }


    소괄호가 난무하는것은 *(간접지정연산자)가 덧셈 뺄셈 연산자보다 우선순위가 높기 때문입니다. 그렇기 때문에 주의하셔야 합니다.
    포인터에 먼저 증감 연산을 하여 주소를 이동시킨후 가리켜서 값에 접근해야 합니다.


    연습문제

    다음 코드에서 빈칸에 들어갈 적절한 포인터 변수를 선언하세요.

    int main(void) {
    	int * arr1[5];
        int * arr2[3][5];
         = arr1;  
         = arr2;  
    	return 0;
    }