본문 바로가기
휴지통/C 프로그래밍

다차원 배열과 포인터의 관계

by 신재권 2021. 1. 21.

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

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

int arr[20]; //arr는 int형 포인터

여기서 arr는 int형 포인터이다. 따라서 다음과 같이 함수의 인자로 전달되기 위해서는,

SimpleFunc(arr);

함수의 매개변수가 다음과 같이 int형 포인터로 선언되어야 한다.

void SimpleFunc(int *ptr){...}

2차원 배열의 이름이 더블 포인터 형이라는 오해를 절대로 해서는 안된다.

-2차원 배열이름이 가리키는 것?

int arr[3][3];

arr가 가리키는 것은 인덱스 기준으로 [0][0]에 위치한(2차원 배열에서도 이를 첫번째 요소라 한다)첫번째 요소이다.

그런데 2차원 배열의 경우 arr[0], arr[1], arr[2]도 의미를 갖는다. 이들은 각각 1행, 2행, 3행의 첫번째 요소를 가리킨다.

따라서 2차원 배열의 첫 번째 요소의 주소 값을 출력하기 위해서는 다음 두 가지 형태의 문장을 구성해 볼수 있다.

printf("%p", arr);

printf("%p", arr[0]);

실제로 위의 두문장의 출력결과는 동일하다.

배열이름 arr를 대상으로 sizeof연산을 하는 경우 배열 전체의 크기를 반환한다.

arr[0], arr[1], arr[2]를 대상으로 sizeof연산을 하는 경우 각 행의 크기를 반환한다.

arr는 첫 번째요소를 가리키면서 배열 전체를 의미한다. 하지만 arr[0]는 첫번째 요소를 가리키되 1행만을 의미한다. 그래서 sizeof연산의 결과가 다른 것이다. 즉, arr2와 arr[0]은 서로 다른 것이다.

-배열이름 기반의 포인터 연산: 배열 이름에 1을 더한 결과?

int arr[3]; //arr는 int형 포인터

double darr[3]; //darr는 double형 포인터

위 선언된 두 배열을 대상으로 다음 문장을 실행하면 출력결과는 어떻게 되는가?

printf("%p", arr+1);

printf("%p", darr+1);

배열이름 arr는 int형 포인터 이므로 arr+sizeof(int)의 계산결과가 출력된다.

배열이름 darr는 double형 포인터 이므로 darr+sizeof(double)의 계산 결과가 출력된다.

이렇듯 '포인터 형'은 포인터의 대상의 증가 및 감소 연산의 결과를 결정짓는 중요한 요소이다. 따라서 다음과 같이 정리 할수 있다.

"두 포인터의 포인터형이 같다면, 두 포인터를 대상으로 하는 증가 및 감소 연산의 결과로 증가 및 감소하는 값의 크기는 동일하다."

2차원 배열이름을 대상으로 증가 및 감소연산을 할 경우, 연산결과는 각 행의 첫 번째 요소의 주소값이 된다. 즉 arr이 1행의 첫번째 요소를 가리키면 arr+1이 반환하는 주소값은 2행의 첫번째 요소를 가리키게 되고, arr+2가 반환하는 주소 값은 3행의 첫번째 요소를 가리키게 된다.

때문에 2차원 배열을 이루는 요소의 자료형이 동일하더라도 배열의 가로길이가 다르면 배열이름을 대상으로 하는 포인터 연산의 결과는 달라진다.

2차원 배열은 가로 길이(열의 길이)에 따라 포인터 연산의 결과는 달라진다.

-최종결론, 2차원 배열이름의 포인터 형

2차원 배열 이름의 포인터 형에는 다음 두가지 정보가 함께 담겨야 한다.

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

배열이름(포인터)를 대상으로 값을 1 증가 및 감소 시 실제로는 얼마가 증가 및 감소하는가?

int arr[3][4]의 포인터 형을 묻는다면

배열 이름 arr이 가리키는 대상은 int형 변수이고, arr의 값을 1증가하면 실제로는 sizeof(int)x4의 크기만큼 주소값이 증가하는 포인터 형이다.

->

arr은 가리키는 대상이 int형 변수이고, 포인터 연산시 sizeof(int)x 4 크기단위로 값이 증가 및 감소하는 포인터 형이다.

가리키는 대상이 int형 변수이면서 포인터 연산시 sizeof(int)x4의 크기단위로 증가 및 감소하는 포인터 변수 ptr은 다음과 같이 선언한다.

int(*ptr)[4];

이것은 포인터 변수의 선언이다. ptr이라는 이름의 포인터 변수를 선언한 것이다.

(*ptr): ptr은 포인터

int : int형 변수를 가리키는 포인터

[4] : 포인터 연산시 4칸씩 건너뛰는 포인터

변수 ptr앞에 *선언은 ptr이 포인터 선언이 되게 한다. 그리고 [4]는 포인터 연산시 4칸씩 건너뛰되, ptr이 가리키는 변수가 int형 변수이기 때문에 int형 변수를 4칸씩 건너뛴다는 의미가 된다. 그리고 위와 같은 포인터 변수는 2차원 배열을 가리키는 용도로만 사용되기 때문에 이러한 유형의 포인터 변수를 가리켜 '배열 포인터 변수'라 한다.

-2차원 배열의 포인터 형을 결정짓는 연습하기

char(*arr1)[4];

arr1은 char형 변수를 가리키면서, 포인터 연산시 sizeof(char)x4의 크기 단위로 값이 증가 및 감소하는 포인터 변수

double(*arr2)[7];

arr2는 double 형 변수를 가리키면서, 포인터 연산시 sizeof(double)x7 의 크기 단위로 값이 증가 및 감소하는 포인터 변수

2. 2차원 배열이름의 특성과 주의 사항

-주의! '배열 포인터'와 '포인터 배열'을 혼동하지 말자.

int *A[4]; //포인터 배열

int (*B)[4] ; //배열 포인터

A는 배열선언이고, B는 포인터 변수 선언이다. 즉 A는 int형 포인터 변수로 이뤄진 int형 포인터 배열이고, B는 열의크기가 4인 int형 2차원 배열을 가리키는 용도의 포인터 변수이다.

-2차원 배열의 함수의 인자로 전달하기

2차원배열의 주소값을 인자로 전달받는 함수를 정의할 수 있다.

int arr[2][7];

double arr2[4][5];

SimpleFunc(arr, arr2);

위 함수의 원형을 선언해 보자

arr의 주소 값을 전달 받을수 있는 매개변수의 이름을 parr1이라 하고, arr2의 주소 값을 전달받을 수 있는 매개변수의 이름을 parr2라 하면, 이 둘은 각각 다음과 같이 선언되어야 한다.

int (*parr1)[7];

double(*parr2)[5];

따라서 SimpleFunc함수는 반환형이 void라고 가정하면 이 함수는 다음과 같이 선언되어야 한다.

void SimpleFunc( int (*parr1)[7], double(*parr2)[5]){...}

이를 대신해서

int parr1[][7], parr2[][5] 도 선언이 가능하다

두 매개변수의 선언은 동일한 선언이다.

이 둘은 매개변수의 선언에서만 같은 의미를 지닌다.

sizeof(arr1)/ sizeof(arr1[0])

sizeof(arr1)의 반환값은 배열의 전체크기이고, sizoof(arr1[0])의 반환값은 배열의 가로크기이다.

따라서 위 연산과정을 통해 배열의 세로 길이가 계산된다.

이는 2차원 배열의 세로 길이 계산시 흔히 사용되는 방법이다.

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

arr[i] == *(arr+i)

arr[2][1] = 4;

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

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

위 세개의 선언은 같은 문장이다.

'휴지통 > C 프로그래밍' 카테고리의 다른 글

문자와 문자열 관련 함수  (0) 2021.01.21
함수 포인터와 void 포인터  (0) 2021.01.21
포인터의 포인터  (0) 2021.01.21
다차원 배열  (0) 2021.01.21
포인터와 함수에 대한 이해  (0) 2021.01.21