suakii.egloos.com

suakii's log





C에서의 2차원 배열과 포인터의 관계 공부

C의 핵심파트라고 할 수 있는 포인터 물론 다른 부분도 많지만 이 포인터가 많아지면 정말 햇갈리기 마련이다.
나 역시 C를 잘알지는 못하지만 볼때마다 햇갈리는 것을 보면 한참 공부가 더 필요한듯 싶구나. 일단은 복잡한 포인터를 읽어나갈 때의 어려움도 있지만 아래의 간단한 코드에서의 오류부터 시작해서 조금씩 이야기를 시작해보려 한다.

자 아래와 같은 간단한 C 코드가 있다고 치자. 2차원 배열 역시 1차원 배열의 연속된 형태이기에 1차원 배열의 단순했던 포인터와 배열표시[] 사이에는 조금 혼동이 오기에 마련이다.

    int arr[3][2] = {1,2,3,4,5,6};
    int* pi = arr;

위의 코드는 에러가 발생하지 않을것 같지만 컴파일을 해보면 오류를 발생시킨다. 이 오류를 왜 발생 하는 것일까? 물론 int (*pi)[2]와 같은 배열 포인터를 사용하면 발생하지 않을 수 도 있지만 말이다. 일단 오류는 접어두고서 아래와 같은 절차를 따라가본다.

우선 2차원 배열은 절대 2차원적으로 메모리에 저장이 되지 않는다. 어차피 선형적인 메모리에 쭉 저장되는것이며 결국은 1차원적인 배열이 2차원적 처럼 보일 뿐이다.

2차원 배열의 배열명 arr은 배열의 제일 첫 번째 원소를 가르키게 된다. 즉 arr[0][0]의 주소인 &arr[0][0]값을 가지게 되는 포인터 상수가 된다.

printf("%d\n", sizeof(arr));

int의 크기가 4바이트일 때 위의 코드의 실행 결과는 24라는 결과를 돌려준다. 즉, sizeof(arr)은 배열 전체의 크기를 돌려주게 된다. 자 그렇다면 sizeof(arr+0)은 얼마의 값을 돌려줄까? 절대 똑같은 결과를 돌려주지 않는다. 0을 더한 연산을 통해서 3개의 부분배열로 구성된 1차원 배열중 첫번째 부분배열의 첫번째 원소를 가르키는 포인터이므로 그 크기는 4를 돌려준다.
글을 쓸수록 점점 햇갈려진다.ㅠ.ㅠ

자 여기서 arr+2를 해보면 arr 배열의 부분 배열 중 두번째 요소를 가리키게 되ㅣ는 것이다. 즉, arr[0], arr[1], arr[2] 중에서 arr[2]두번째 부분 배열을 가르키게 된다. 그러므로 printf("%d\n", sizeof(arr+2)); 를 하게 되면 크기는 4를 돌려주게 된다. 즉 포인터라는 것이지. 앞의 말과 일치하니 다행이다. 즉, 여기서 1을 더하든 2를 더하든 간에 부분 배열의 크기만큼을 건너 뛰게 되는 것이다. 그래서 나온것이  배열 포인터이겠지.(Pointer to Array) 위에서 발생한 에러코드는

int (*pi)[2] = arr;

와 같은 형태로 선언이 된다면 사실 포인터가 가리키는 대상이 배열형으로 구성되어 있으므로 그 증가값의 범위를 알 수 있게 된다.

자 다시 돌아가서,  그렇다면 *(arr+2)는 무엇이란 말인가? arr+2는 세번째 부분배열(1차원)의 시작번지를 가르키는 포인터인데 그렇다면 *(arr+2)는 그 시작번지의 원소값을 가져오게 될까?물론 아니다. 익숙한 일차원 배열에서 arr[n] = *(arr+n)이라는 식을 봤을 것이다. 즉 배열의 n번째의 원소값을 그 배역의 시작위치에서 n만큼 건너뛴 다음 * 연산자를 통해서 그 원소값을 가져오게 되는 것이다. 그렇다면 위에서 질문한  것은

*(arr+2)는 arr[2]라고 쓸 수 있다.


지금 우리가 다루고 있는 배열은 2차원 배열이다. 그러므로 arr[2]는 원소를 2개 가지는 배열이 아니라 arr[3][2]에서 두번째 부분 배열을 나타내는 것이다. 그럼 sizeof(*(arr+2))의 크기는 얼마일까?
당연히 부분배열의 전체 크기를 돌려주게 된다. 그러므로 그 크기는 원소를 2개씩 가지고 있으므로 8이라는 값을 돌려준다.

다시 한번 정리하면 arr+2 는 부분배열의 첫번째 원소에 대한 포인터이고, *(arr+2)는 부분배열 arr[2]인 것이며 이 arr[2]의 값은 arr[2]의 시작위치의 첫번째 원소의 번지 즉, 포인터인것이다. 그러므로 *(*(arr+2))는 바로 arr[2][0]인 것이다.

자 그러면 다시 처음으로 돌아가서 아래의 코드가 에러를 발생한 이유는 그리고 그 오류를 해결하려면

int arr[3][2] = {1,2,3,4,5,6};
int* pi = arr;

arr은 배열 전체를 가르키고 있으니 포인터가 될 수 없다. 즉 우리가 원하는 것은 부분배열 arr[0]을 가르킬 수 있는 포인터를 원한다. arr[0] = *(arr+0) = *arr

즉 위의 코드는 int *pi = *arr;
을 해주어야 한다. 결국 중요한 것은 부분배열의 크기를 미리 알고 있어야지만 건너 뛸 수 있다는것 아니겠는가.

p.s 틀렸을지도 모른다는 생각이 문들 들었다.ㅠ.ㅠ.

트랙백

이 글과 관련된 글 쓰기 (트랙백 보내기)
TrackbackURL : http://suakii.egloos.com/tb/2099897 [도움말]
  • 수아기의 느낌 2008/10/16 14:29 #

    대학원 시험 기출문제 풀다가 포인터 쪽으로 빠졌다가 쓴글 아주 그냥 모르겠다.... more

덧글

덧글 입력 영역