1차원 배열이름의 포인터 형과 2차원 배열이름의 포인터 형
int arr[10]; // arr은 int형 포인터
다음과 같이 함수의 인자로 전달되기 위해서는
simplefunc(arr);
함수의 매개변수가 다음과 같이 int형 포인터로 선언되어야 함
void simplefunc(int* ptr)
다음과 같이 오해할 수 있음
"int형 2차원 배열의 이름은 int형 더블 포인터니(이것이 잘못됨), int형 2차원 배열의 이름을 인자로 전달받으려면, 매개변수로 int형 더블 포인터 변수가 선언되어야겠네?" → X
"2차원 배열의 이름이 더블 포인터 형이라는 오해를 절대로 해서는 안 된다!"
그렇다면 2차원 배열이름의 포인터 형은 무엇?
2차원 배열이름의 포인터 형을 결정짓는 방식은 1차원 배열이름의 포인터 형을 결정짓는 방식과 다르며, 조금 더 복잡하다.
2차원 배열이름이 가리키는 것들은?
int arr2d[3][3];
배열이름 arr2d가 가리키는 것은 인덱스 기준으로 [0][0]에 위치한(2차원 배열에서도 이를 첫 번째 요소라 한다) 첫 번째 요소이다.
2차원 배열의 경우는 arr2d[0], arr2d[1], arr2d[2]도 의미를 갖는다.
이들은 각각 1행, 2행, 3행의 첫 번째 요소를 가리킨다.
2차원 배열의 첫 번째 요소 주소 값 출력을 위해서 다음 두 가지 형태의 문장을 구성 가능
#include <stdio.h>
int main(void)
{
int arr2d[3][3];
printf("%p\n", arr2d);
printf("%p\n", arr2d[0]);
return 0;
}
두 문장의 출력결과는 동일
그렇다면 arr2d, arr2d[0]은 같은 것인가?
위 질문에 대한 실험 예제
#include <stdio.h>
int main(void)
{
int arr2d[3][3];
printf("%d \n", arr2d);
printf("%d \n", arr2d[0]);
printf("%d \n\n", &arr2d[0][0]);
printf("%d \n", arr2d[1]);
printf("%d \n\n", &arr2d[1][0]);
printf("%d \n", arr2d[2]);
printf("%d \n\n", &arr2d[2][0]);
printf("sizeof(arr2d) : %d \n", sizeof(arr2d));
printf("sizeof(arr2d[0]) : %d \n", sizeof(arr2d[0]));
printf("sizeof(arr2d[1]) : %d \n", sizeof(arr2d[1]));
printf("sizeof(arr2d[2]) : %d \n", sizeof(arr2d[2]));
return 0;
}
- 배열이름 arr2d를 대상으로 sizeof 연산을 하는 경우 배열 전체의 크기를 반환
- arr2d[0], arr2d[1], arr2d[2]를 대상으로 sizeof 연산을 하는 경우 각 행의 크기를 반환
"
arr2d는 첫 번째 요소를 가리키면서 배열 전체를 의미한다.
하지만, arr2d[0]는 첫 번째 요소를 가리키되 1행만을 의미한다.
따라서 sizeof 연산의 결과가 다른 것이다.
즉, arr2d와 arr2d[0]는 서로 다른 것이다."
배열이름 기반의 포인터 연산: 배열이름에 1을 더한 결과는?
1차원으로 우선 생각
int iarr[3]; //iarr은 int형 포인터
double darr[3]; //darr은 double형 포인터
선택된 두 배열을 대상으로 다음의 문장을 실행하면?
printf("%p", iarr+1)
printf("%p", darr+1)
- "배열이름 iarr은 int형 포인터이므로 iarr+sizeof(int) 계산결과가 출력된다."
- "배열이름 darr은 double형 포인터이므로 darr+sizeof(double) 계산결과가 출력된다."
'포인터 형'은 포인터 대상의 증가 및 감소연산의 결과를 결정짓는 중요한 요소.
따라서 다음과 같이 정리 가능.
"두 포인터의 포인터 형이 같다면, 두 포인터를 대상으로 하는 증가 및 감소연산의 결과로 증가 및 감소하는 값의 크기는 동일하다."
#include <stdio.h>
int main(void)
{
int arr1[3][2];
int arr2[2][3];
printf("arr1 : %p \n", arr1);
printf("arr1[0][1] : %p \n", &(arr1[0][1]));
printf("arr1+1 : %p \n", arr1+1);
printf("arr1+2 : %p \n", arr1+2);
printf("arr2: %p \n", arr2);
printf("arr2+1: %p \n", arr2+1);
return 0;
}
arr1을 대상으로 값을 1씩 증가시킬 때마다 8씩 증가하고,
arr2를 대상으로 값을 1씩 증가시킬 때마다 12씩 증가함을 알 수 있다.
2차원 배열이름을 대상으로 증가 및 감소연산을 할 경우, 연산결과는 각 행의 첫 번째 요소의 주소 값이 된다.
즉, arr1이 1행의 첫 번째 요소를 가리키면,
arr1+1이 반환하는 주소 값은 2행의 첫 번째 요소를 가리키게 되고,
arr1+2가 반환하는 주소 값은 3행의 첫 번째 요소를 가리키게 됨.
때문에 2차원 배열을 이루는 요소의 자료형이 동일하더라도 배열의 가로길이(열)이 다르면, 포인터 연산의 결과는 달라진다.
"그렇다면 배열이름 arr1과 arr2는 둘 다 int형 2차원 배열인데도 가로길이가 다르다는 이유만으로 포인터 형이 다른 것임?" → 다름!
2차원 배열이름의 포인터 형은 가로의 길이에 따라서도 달라진다! 그리고 그것이 1차원 배열이름의 포인터 형을 결정하는 것과 달리, 2차원 포인터 형 결정을 어렵게 하는 이유가 된다.
최종결론! 2차원 배열이름의 포인터 형
다음 두 가지 정보가 함께 담겨야 한다.
- 가리키는 대상은 무엇인가?
- 배열이름(포인터)를 대상으로 값을 1 증가 및 감소 시 실제로는 얼마가 증가 및 감소하는가?
다음 배열 이름의 포인터 형
int arr[3][4];
"배열이름 arr이 가리키는 대상은 int형 변수이고, arr의 값을 1 증가하면 실제로는 sizeof(int)x4의 크기만큼 주소 값이 증가하는 포인터 형" ≫ "arr은 가리키는 대상이 int형 변수이고, 포인터 연산 시 sizeof(int) x 4의 크기단위로 값이 증가 및 감소하는 포인터 형이다"
위에 대한 포인터 변수 ptr은 다음과 같이 선언
int(*ptr)[4] → 포인터 변수의 선언
ptr이라는 이름의 포인터 변수를 선언
int는 가리키는 대상의 자료형
[4]는 한번에 건너뀔 변수의 개수를 의미
ptr앞의 * 선언은 ptr이 포인터 선언이 되게 한다.
[4]는 포인터 연산 시 4칸씩 건너뛰되,
ptr이 가리키는 변수가 int형 변수이기 때문에 int형 변수를 4칸씩 건너뛴다.
위와 같은 포인터 변수는 2차원 배열을 가리키는 용도로만 사용되기 때문에 이러한 유형의 포인터 변수를 가리켜 '배열 포인터 변수'
2차원 배열의 포인터 형을 결정짓는 연습하기
char(*arr1)[4];
double(*arr2)[7];
"arr1은 char형 변수를 가리키면서, 포인터 연산 시 sizeof(char) x 4의 크기로 값이 증가 및 감소 하는 포인터 변수"
"arr2은 double형 변수를 가리키면서, 포인터 연산 시 sizeof(double) x 7의 크기로 값이 증가 및 감소 하는 포인터 변수"
예제
#include <stdio.h>
int main(void)
{
int arr1[2][2] = {
{1, 2},
{3, 4}
};
int arr2[3][2] = {
{1, 2},
{3, 4},
{5, 6}
};
int arr3[4][2] = {
{1, 2},
{3, 4},
{5, 6},
{7, 8}
};
int(*ptr)[2]; // int형을 가리키고, 포인터 연산 시 sizeof(int) x 2의 크기로 값이 변하는 포인터 변수 ptr
ptr = arr1; // 포인터 변수에 배열 arr1의 주소를 가리키게 함
printf("** Show 2,2 arr1 ** \n");
for (int i = 0; i < 2; i++)
printf("%d %d \n", ptr[i][0], ptr[i][1]);
ptr = arr2; // 포인터 변수에 배열 arr2의 주소를 가리키게 함
printf("** Show 3,2 arr1 ** \n");
for (int i = 0; i < 3; i++)
printf("%d %d \n", ptr[i][0], ptr[i][1]);
ptr = arr3; // 포인터 변수에 배열 arr3의 주소를 가리키게 함
printf("** Show 4,2 arr1 ** \n");
for (int i = 0; i < 4; i++)
printf("%d %d \n", ptr[i][0], ptr[i][1]);
return 0;
}
참고 : [윤성우 열혈 C 프로그래밍] - 대부분의 내용 및 코드는 이 책에서 개인 공부 정리 목적으로 참고하였습니다.
'Language&Framework&Etc > C' 카테고리의 다른 글
함수 포인터와 void 포인터(19-1) 함수 포인터와 void 포인터 (0) | 2020.12.10 |
---|---|
다차원 배열과 포인터의 관계(18-2) 2차원 배열이름의 특성과 주의사항 (0) | 2020.12.10 |
포인터의 포인터(17-2) 다중 포인터 변수와 포인터의 필요성 (0) | 2020.12.10 |
포인터의 포인터(17-1) 포인터의 포인터에 대한 이해 (0) | 2020.12.09 |
다차원 배열(16-2) 3차원 배열 (0) | 2020.12.09 |