본문 바로가기

Books/열혈강의 C 프로그래밍

[열혈C] 04. Part3 Chapter18 다차원 배열과 포인터의 관계


이번 장의 포인터의 마지막 난관이다. 그만큼 깊이 있는 이해를 요구한다.
한번에 이해되지 않는다면 서너 번 봐서 이해하자.

18장 내용 정리

2차원 배열 이름의 포인터 타입

14장에서는 1차원 배열을 함수의 인자로 전달하는 방법에 대해 이야기하였다.

아래 그림에서처럼, 함수로 전달되는 배열의 이름 arr은 int형 포인터(int *)로 받을 수 있다.

int형 변수를 요소로 지니는 1차원 배열의 이름 역시 int형 포인터이기 때문에 가능한 일이다.


2차원 배열의 이름은 더블 포인터로 받으면 될까? No...


  1. 2차원 배열 이름이 가리키는 것은?
    1차원 배열에서는 배열 이름이 가리키는 요소가 배열 이름의 포인터 타입을 결정짓는 기준이었다.
    마찬가지로 2차원 배열에서도 배열 이름이 가리키는 요소가 배열 이름의 포인터 타입을 결정 짓는 기준의 일부가 된다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #include <stdio.h>
    int main(void)
    {
        int i;
        int a[3][2= {123456};
     
        printf("a[0] : %d \n", a[0]);
        printf("a[1] : %d \n", a[1]);
        printf("a[2] : %d \n", a[2]);
     
        printf("a    : %d \n", a);
        return 0;
    }
     
    /*
    # 실행 결과
    # a[0], a[1] 그리고 a[2]가 나타내는 값을 10진수 정수형으로 출력하고 있다.
    # 다른 것은 몰라도, 지니고 있는 값의 차가 8이라는 것은 알 수 있다.
    a[0] : 5240988
    a[1] : 5240996
    a[2] : 5241004
    a    : 5240988
    */
    cs

    2차원적인 메모리 구조로 이야기한다면 각 행의 첫 번째 요소를 가리킨다고 말할 수 있다.


    이제 배열 이름 a의 포인터 타입을 이야기 해보자.
    쉽게 생각해서 a가 가리키는 요소가 배열의 첫 번째 요소(a[0][0])이므로, int형 포인터라고 결론 내릴 수 있다. 왜? 배열의 첫 번째 요소가 int형 변수이기 때문이다.

    하지만 이것이 끝이 아니다. 배열 이름 a의 포인터 타입은 조금 더 구체화되어야 한다.

  2. 2차원 배열 이름 + 1 : 배열 이름을 이용한 포인터 연산
    2차원 배열 이름도 배열의 첫 번째 요소를 가리키는 포인터이므로, 배열의 이름이 지니는 값을 하나 증가시키면 어딘가 새로운 곳을 가리키게 될 것이다. 참고로 2차원 배열 이름의 포인터 타입을 완벽히 발히는데 상당히 중요한 정보가 되는 내용이다.  

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include <stdio.h>
    int main(void)
    {
        int a[3][2= {123456};
     
        printf("a   : %d \n", a);
        printf("a+1 : %d \n", a+1);
        printf("a+2 : %d \n", a+2);
     
        return 0;
    }
     
    /*
    # 실행 결과
    # 포인터 값을 증가시킬 때마다 가리키는 위치는 8byte씩 이동하고 있다.
    a   : 14350868
    a+1 : 14350876
    a+2 : 14350884
    */
    cs

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


  3. 포인터의 타입에는 이동에 대한 정보가 들어 있다.
    기억하자! int형 포인터라고 해서 모두 같은 것이 아니다. 
    포인터 연산 시 증가 및 감소되는 값에 따라서 그 타입은 달라진다.  
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    #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("\n");
           printf("arr2   : %d \n", arr2);
           printf("arr2+1 : %d \n", arr2+1);
           printf("arr2+2 : %d \n", arr2+2);
           return 0;
    }
     
    /*
    # 실행 결과
    # 포인터 연산 시 증가되는 값이 차이를 보인다.
    # arr1은 int형 포인터(int *)이자, 포인터 연산 시 배열 요소를 2칸씩(2x4byte) 건너 뛰는 포인터
    # arr2는 int형 포인터(int *)이자, 포인터 연산 시 배열 요소를 3칸씩(3x4byte) 건너 뛰는 포인터
    arr1   : 3865408
    arr1+1 : 3865416
    arr1+2 : 3865424
    arr2   : 3865376
    arr2+1 : 3865388
    arr2+2 : 3865400
    */
    cs


  4. 최종 결론! 배열 이름의 포인터 타입
    배열 포인터 : 배열을 가리킬 수 있는 포인터  
    1
    2
    # arr은 포인터 연산 시 배열 요소를 4칸씩(4x4byte) 건너뛰는 int형 싱글 포인터
    int arr[2][4];
    cs

    완전히 일치하는 포인터 변수를 선언하려면?
    1
    2
    3
    4
    5
    # (*pArr) : 포인터 선언임을 말함
    int : int형 변수를 가리키는 포인터라는 뜻
    # [4] : 포인터 연산에 따른 증가 혹은 감소의 폭을 이야기 한다.
     
    int (*pArr)[4];
    cs


int (*pArr)[4] 과 int* pArr[4]의 차이점

int (*pArr)[4]는 배열을 가리키는 포인터(배열 포인터)다.

int* pArr[4]는 int형 변수의 주소 값 4개를 저장할 수 있는 배열(포인터 배열)이다.


연습문제 18-2



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

이번에 소개하는 내용은 포인터와 배열의 개념이 어느 정도 선 다음에 봐도 절대 늦지 않는다.

부담되면 일단은 그냥 넘어가도 좋다.


13장 요약

"arr[i] == *(arr+1)"는 arr이 포인터 변수이거나 배열 이름인 경우 항상 성립.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>int main(void)
{
    int arr[3][2= { { 12 },{ 34 },{ 56 } };       
    printf("arr[0]   : %d \n", arr[0]);       
    printf("*(arr+0) : %d \n"*(arr + 0));       
    printf("\n");       
 
    printf("arr[1]   : %d \n", arr[1]);       
    printf("*(arr+1) : %d \n"*(arr + 1));       
    printf("\n");       
 
    printf("arr[2]   : %d \n", arr[2]);       
    printf("*(arr+2) : %d \n"*(arr + 2));       
    printf("\n");       
 
    printf("%d, %d \n", arr[1][0], (*(arr + 1))[0]);       
    printf("%d, %d \n", arr[1][2], *(arr[1+ 2));       
    printf("%d, %d \n", arr[2][1], *(*(arr + 2+ 1));       
    return 0;
}
 
/*
# 실행 결과
arr[0]   : 7796984
*(arr+0) : 7796984
arr[1]   : 7796992
*(arr+1) : 7796992
arr[2]   : 7797000
*(arr+2) : 7797000
3, 3
5, 5
6, 6
*/
cs


다 같은 의미

1
2
3
4
a[i][j]
*(a[i]+j)
(*(a+i))[j]
*(*(a+i)+j)
cs




열혈강의 C 프로그래밍
국내도서
저자 : 윤성우
출판 : 프리렉 2003.12.15
상세보기