1차원 배열이름의 포인터 형과 2차원 배열이름의 포인터 형은 다르기 때문에, 연장선이라고 생각하면 큰일난다.
지금 하는 것은 앞 서 본 적이 없는 새로운 내용이다. 나도 공부하면서 헷갈렸다. 꼭 머릿속에 박힐 때 까지 복습 또 복습을 하고 넘어가자 !
2차원 배열이름이 가리키는 것들
int arr2d[3][3];
위처럼 선언된 2차원 배열이 있다. 배열이름 arr2d가 가리키는 것은 인덱스 기준으로 [0][0]에 위치한 첫 번째 요소이다.
그런데 2차원 배열의 경우는 arr2d[0], arr2d[1], arr2d[2]도 의미를 갖는다. 이들은 각각 1행, 2행, 3행의 첫 번째 요소를 가리킨다.
따라서 주소값을 출력해보면 arr2d와 arr2d[0]의 주소값은 동일하다.
그럼 arr2d와 arr2d[0]은 같은 것인가 의문이 들 수 있다.
ex)
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() { 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; } | cs |
출력결과
4585464
4585464
4585464
4585476
4585476
4585488
4585488
sizeof(arr2d): 36
sizeof(arr2d[0]): 12
sizeof(arr2d)[1]: 12
sizeof(arr2d)[2]: 12
배열이름 arr2d를 대상으로 sizeof연산을 하는 경우 배열 전체의 크기를 반환
arr2d[0],arr2d[1],arr2d[2]를 대상으로 sizeof 연산을 하는 경우 각 행의 크기를 반환
arr2d는 첫번째 요소를 가리키면서 배열 전체를 의미하지만, arr2d[0]는 첫번째 요소를 가리키되 배열의 1행만을 의미한다. 그래서 sizeof 연산의 결과가 다른 것이다.
배열이름에 1을 더한 결과
int iarr[3]; // iarr은 int형 포인터
double darr[7]; // darr은 double형 포인터
위의 두 문장은 저번에 공부했던 내용이다. printf("%p %p", iarr+1, darr+1);을 하면 각각 sizeof(int), sizeof(double)의 계산결과가 출력된다.
이렇듯 포인터 형은 포인터 대상의 증가 및 감소연산의 결과를 결정짓는 중요한 요소이다.
"두 포인터의 포인터 형이 같다면, 두 포인터를 대상으로 하는 증가 및 감소연산의 결과로 증가 및 감소하는 값의 크기는 동일하다."
ex) 그렇다면 2차원 배열이름을 대상으로 증가 연산을 진행해보자
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include<stdio.h> int main() { int arr1[3][2]; int arr2[2][3]; printf("arr1: %p \n", arr1); 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; } | cs |
출력결과
arr1: 0x7ffeefbff5a0
arr1+1: 0x7ffeefbff5a8
arr1+2: 0x7ffeefbff5b0
arr2 0x7ffeefbff580
arr2+1: 0x7ffeefbff58c
arr1은 8씩 증가하였고, arr2는 12가 증가하였다. arr1과 arr2 대상의 포인터 연산결과는 다음과 같다.
이렇게 2차원 배열이름을 대상으로 증가 및 감소연산을 할 경우, 각 행의 첫 번째 요소의 주소 값이 된다.
즉 arr1이 1행의 첫 번째 요소를 가리키면, arr1+1이 반환하는 주소 값은 2행의 첫 번째 요소를 가리키게 된다.
때문에 2차원 배열을 이루는 요소의 자료형이 동일하더라도 배열의 가로길이가 다르면 배열이름을 대상으로 하는 포인터 연산의 결과가 달라진다. 즉 포인터 형은 가로의 길이에 따라서도 달라진다!!
int arr[3][4];
위의 배열이름의 포인터 형을 묻는다면,
arr은 가리키는 대상이 int형 변수이고, 포인터 연산 시 sizeof(int) * 4의 크기단위로 값이 증가 및 감소하는 포인터 형이다.
그렇다면 포인터 변수의 선언을 해보자.
int (*ptr) [4];
ptr은 int형 변수를 가리키면서, 포인터 연산 시 sizeof(int) * 4(가로길이)의 크기단위로 값이 증가 및 감소하는 포인터 변수
이것이 포인터 변수의 선언이다.
ex)
char (*arr1) [4];
- arr1은 char형 변수를 가리키면서, 포인터 연산 시 sizeof(char) * 4의 크기단위로 값이 증가 및 감소하는 포인터 변수
double (*arr2) [7];
- arr2는 double형 변수를 가리키면서, 포인터 연산 시 sizeof(double) * 8의 크기단위로 값이 증가 및 감소하는 포인터 변수
ex)
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 | #include<stdio.h> int main() { int arr1[2][2]={1,2,3,4}; int arr2[3][2]={1,2,3,4,5,6}; int i; int (*ptr)[2]; ptr = arr1; for(i=0;i<2;i++) { printf("%d %d \n", ptr[i][0], ptr[i][1]); } ptr = arr2; for(i=0;i<3;i++) { printf("%d %d \n", ptr[i][0], ptr[i][1]); } return 0; } | cs |
출력결과
1 2
3 4
1 2
3 4
5 6
8행 : 가로 길이가 동일하니, 이들 배열이름의 포인터 형은 모두 동일하다.
2차원 배열이름의 특성과 주의사항
1. 배열포인터와 포인터배열
int *arr1[5]; // 포인터배열
int(*arr2) [5]; // 배열포인터
ex)
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 | #include<stdio.h> int main() { int num1=10, num2=20, num3=30; int arr2d[2][4]={1,2,3,4,5,6,7,8}; int i,j; int *arr1[]={&num1,&num2,&num3}; // 포인터 배열 int (*arr2)[4]=arr2d; // 배열 포인터 printf("%d %d %d \n", *arr1[0], *arr1[1], *arr1[2]); for(i=0;i<2;i++) { for(j=0;j<4;j++) { printf("%d", arr2[i][j]); } printf("\n"); } return 0; } | cs |
10 20 30
1234
5678
포인터배열과 배열포인터가 헷갈릴 때 참조하면 좋겠다!
2. 2차원 배열을 함수의 인자로 전달하기
int main()
{
int arr1[2][5];
double arr[4][3];
Simple(arr1, arr2);
. . . .
}
이처럼 작성되었다면, 매개변수는 다음과 같이 선언되어야 한다.
void Simple(int (*parr1)[5], double (*parr2)[3]) { . . . . }
이를 대신해 이처럼 작성이 가능하다.
void Simple(int parr1[][5], double parr2[][3]) { . . . . }
두 문장은 완전히 같은 것이다.
ex)
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 45 | #include<stdio.h> void ShowArr(int (*arr)[4],int column) // 배열요소 전체출력 { int i,j; for(i=0;i<column;i++) { for(j=0;j<4;j++) { printf("%d",arr[i][j]); } printf("\n"); } } int Sum(int arr[][4],int column) // 배열요소의 합 반환 { int i,j,sum=0; for(i=0;i<column;i++) { for(j=0;j<4;j++) { sum += arr[i][j]; } } return sum; } int main() { int arr1[2][4]={1,2,3,4,5,6,7,8}; int arr2[3][4]={1,1,1,1,3,3,3,3,5,5,5,5}; ShowArr(arr1,sizeof(arr1)/sizeof(arr1[0])); ShowArr(arr2,sizeof(arr2)/sizeof(arr2[0])); printf("arr1의 합: %d \n", Sum(arr1,sizeof(arr1)/sizeof(arr1[0]))); printf("arr2의 합: %d \n", Sum(arr2,sizeof(arr1)/sizeof(arr1[0]))); return 0; } | cs |
1234
5678
1111
3333
5555
arr1의 합: 36
arr2의 합: 16
3행, 17행 : 두 매개변수의 선언에 주목하면, 첫 번째 매개변수는 전달을 받을 때 받는 방법이고, 두번째를 유심히 봐야한다. 두 번째 매개변수를 통해 배열의 세로길이를 전달받는다. 때문에 위 두 함수 모두 가로길이가 4인 int형 2차원 배열이라면 세로길이에 상관없이 인자로 전달이 가능하며, 또 그에 따라서 적절히 동작하도록 함수가 정의되었다.
sizeof(arr1) / sizeof(arr1[0])
sizeof(arr2) / sizeof(arr2[0])
sizeof(arr1)과 sizeof(arr2)의 반환 값은 배열의 전체크기이고, sizeof(arr1[0])와 sizeof(arr2[0])의 반환 값은 배열의 가로크기 이다. 따라서 위의 연산과정을 통해 배열의 세로길이가 계산된다.
3. 2차원 배열에서도 arr[i] == *(arr+i) 는 같다.
arr[2][1] = 4;
1) >> (arr[2])[1]) = 4; >> (*(arr+2))[1] = 4;
2) >> (*(arr+2))[1] = 4; >> A[1] = 4; >> *(A+1) = 4; >> *(*(arr+2)+1) = 4;
3) >> arr[2][1] = 4; >> A[1] = 4; >> *(A+1) = 4; >> *(arr[2]+1) = 4;
'programming > C' 카테고리의 다른 글
C // 문자와 문자열관련함수 (0) | 2017.12.28 |
---|---|
C // 함수포인터와 void포인터 (0) | 2017.12.27 |
C // 포인터의 포인터 (0) | 2017.12.23 |
C // 다차원배열 (0) | 2017.12.22 |
C // 포인터와 함수 (0) | 2017.12.21 |