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] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
double arr2[4][4] = {
{1.1, 2.1, 3.1, 4.1},
{5.1, 6.1, 7.1, 8.1},
{9.1, 10.1, 11.1, 12.1},
{13.1, 14.1, 15.1, 16.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] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
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] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
double arr2[2][3] = {
{1.1, 2.2, 3.3},
{4.4, 5.5, 6.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 |