본문 바로가기

2차원 배열과 포인터

1. 2차원 배열 이름의 포인터 형

 

1차원 배열 이름은 상수 싱글 포인터다. 

그럼 2차원 배열 이름은 더블 포인터일까?

다음과 같이 선언된 2차원 배열이 있다고 하자.

 

int arr[3][3];

 

당연히 아니다. 2차원 배열의 이름은 인덱스 기준으로 [0][0]의 주소, 즉 시작 주소다.

또한  arr[n] 은  arr 의  n 행의 시작 주소를 의미한다.

그렇다면  arr[0] 과  arr 은 같은 걸까?

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
 
int main()
{
    int arr[3][3];
 
    printf("arr: %p\n", arr);
    printf("arr[0]: %p\n", arr[0]);
    printf("&arr[0][0]: %p\n\n"&arr[0][0]);
 
    printf("arr[1]: %p\n", arr[1]);
    printf("&arr[1][0]: %p\n\n"&arr[1][0]);
 
    printf("arr[2]: %p\n", arr[2]);
    printf("&arr[2][0]: %p\n\n"&arr[2][0]);
 
    printf("sizeof(arr): %d\n"sizeof(arr));
    printf("sizeof(arr[0]): %d\n"sizeof(arr[0]));
 
    return 0;
}
cs

 

 

 arr 은 첫 번째 요소( arr[0][0] )를 가리키면서 배열 전체를 의미한다.

하지만  arr[0] 은 첫 번째 행만을 의미한다. 그래서  sizeof  연산의 결과가 다른 것이다.

 

 

마찬가지로 다음과 같은 2차원 배열이 있다고 하자.

 

int arr[3][3];

 

 arr 이 0x10001000이라면  arr+1 의 출력 결과는 뭘까?

0x1000100C일까, 0x10001004일까?

 

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
 
int main()
{
    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;
}
cs

 

 

 arr 의 열 수(가로 길이) × sizeof(int) = 12만큼 증가함을 알 수 있다.

 

포인터 형에는 다음의 두 가지 정보가 포함되어 있어야 한다.

 

① 가리키는 대상은 무엇인가?

② 배열 이름을 대상으로 증감 연산 시 실제로 얼마나 증감되는가?

 

그런데 2차원 배열에서 조건 ②은 열 수에 따라 달라진다.

따라서 2차원 배열 이름의 포인터 형은 열 수에 따라 결정된다고 할 수 있다.

 

결론적으로, 2차원 배열의 포인터 형은 다음과 같다.

 

TYPE (*NAME) [COLUMNS];

 

 

예를 들어 위의 배열  arr 의 포인터 형은 다음과 같다.

 

int(*ptr)[4];

 

이는 int형 변수를 가리키는  ptr 이 포인터 변수이며 연산 시 4칸(sizeof(int) × 4)씩 건너뛴다는 것을 의미한다. 

다시 말해, *에 의해  ptr 은 포인터 선언이 되고,  int 에 의해 int형을 가리키는 포인터 선언이 되고,  [4] 에 의해 int형 변수 4개를 건너뛴다는 의미가 되는 것이다.

 

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
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
 
int main()
{
    int arr1[3][4= {
        {1234},
        {5678},
        {9101112}
    };
    double arr2[4][4= {
        {1.12.13.14.1},
        {5.16.17.18.1},
        {9.110.111.112.1},
        {13.114.115.116.1}
    };
    int(*ptr1)[4];
    double(*ptr2)[4];
    int i = 0, j = 3;
 
    ptr1 = arr1;
    ptr2 = arr2;
    
    for (i = 0; i < 3++i)
    {
        for (j = 0; j < 4++j)
        {
            printf("ptr1[%d][%d]: %-2d    ", i, j, ptr1[i][j]);
        }
        printf("\n");
    }
    
    printf("\n");
    for (i = 0; i < 4++i)
    {    
        for (j = 0; j < 4++j)
        {
            printf("ptr2[%d][%d]: %-4.1f    ", i, j, ptr2[i][j]);
        }
    }
 
    return 0;
}
 
cs

 

 

 

 

 

2. 포인터 배열과 배열 포인터

 

다음 두 선언의 차이는 무엇일까?

 

int* ptr1[4];

int (*ptr2)[4];

 

전자는 주소값을 저장하는 배열, 포인터 배열이고

후자는 2차원 배열의 포인터, 배열 포인터다. 좀 더 엄밀히 말하면 열 수가 4인 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
25
26
27
28
29
30
31
#include <stdio.h>
 
int main()
{
    int arr2[3][4= {
        {1234},
        {5678},
        {9101112}
    };
    int num1 = 10, num2 = 20, num3 = 30, num4 = 40;
    int* ptr1[4= { &num1, &num2, &num3, &num4};
    int(*ptr2)[4= arr2;
    int i, j;
 
    for (i = 0; i < sizeof(ptr1) / sizeof(int*); ++i)
    {
        printf("ptr1[%d]: %-2d    ", i, *(ptr1[i]));
    }
 
    printf("\n\n");
    for (i = 0; i < 3++i)
    {
        for (j = 0; j < 4++j)
        {
            printf("ptr2[%d][%d]: %-2d    ", i, j, ptr2[i][j]);
        }
        printf("\n");
    }
 
    return 0;
}
cs

 

 

 

 

 

 

3. 2차원 배열을 함수의 매개변수로 전달하기

 

다음과 같이 2차원 배열이 선언되어 있다고 하자.

 

int arr1[3][4];

double arr2[2][3];

 

그리고  func 라는 함수로 이 두 배열을 전달하려고 한다고 하자(반환형은 void라 가정).

 func  함수는 어떻게 선언되어야 할까?

 

2차원 배열은 배열 포인터 변수에 저장된다. 따라서 다음과 같이 선언되어야 한다.

 

void func(int(*arr1)[4], double(*arr2)[3]);

 

이는 다음과 동일한 선언이다.

 

void func(int arr1[][4], double arr2[][3]);

 

왜냐하면 다음 선언은 동일한 선언이기 때문이다.

 

int(*arr1)[4]

int arr1[][4]

 

double(*arr2)[3]

double arr2[][3]

 

물론 이는 매개변수일 때에 국한된다. 함수 내에서 선언될 때는 다르다.

 

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
34
35
36
37
38
39
40
41
42
43
44
#include <stdio.h>
 
void func(int(*arr1)[4], double(*arr2)[3]);
 
int main()
{
    int arr1[3][4= {
        {1234},
        {5678},
        {9101112}
    };
    double arr2[2][3= {
        {1.12.23.3},
        {4.45.56.6}
    };
 
    func(arr1, arr2);
 
    return 0;
}
 
void func(int(*arr1)[4], double(*arr2)[3])
{
    int i, j;
 
    for (i = 0; i < 3++i)
    {
        for (j = 0; j < 4++j)
        {
            printf("%-2d  ", arr1[i][j]);
        }
        printf("\n");
    }
 
    printf("\n\n");
    for (i = 0; i < 2++i)
    {
        for (j = 0; j < 3++j)
        {
            printf("%-4.1f  ", arr2[i][j]);
        }
        printf("\n");
    }
}
cs

 

 

 

 

 

 

4. 2차원 배열에서도 arr[i]와 *(arr + i)는 같다.

 

다음과 같이 2차원 배열이 선언되었다고 하자.

 

int arr[3][3];

 

앞에서 같은 배열을 예로 들어 다음과 같은 사실을 언급했다.

 

① arr[n]은 arr의 n행의 시작주소다.

② arr + n을 계산하면 arr의 열 수 × sizeof(int) 만큼 증가한다.

 

따라서 2차원 배열에서도  arr[i]와  *(arr+i) 가 같음을 알 수 있다.

 

예를 들어  arr[2][1] = 3; 은 다음으로 대체할 수 있다.

 

*(arr[2] + 1) = 3;

*(*(arr + 2) + 1) = 3;

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
 
int main()
{
    int arr[4][5= { 0 };
 
    *(arr[2+ 3= 3;
    printf("arr[2][1]: %d\n", arr[2][1]);
 
    *(*(arr + 3+ 2= 4;
    printf("arr[3][2]: %d\n", arr[3][2]);
 
    return 0;
}
 
cs

 

 

'Programming > C' 카테고리의 다른 글

void형 포인터  (0) 2019.04.15
함수 포인터  (0) 2019.04.10
다차원 배열  (0) 2019.04.01
다중 포인터  (0) 2019.03.24
매개변수  (0) 2019.03.24