ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 함수로 배열을 인자로 넘길때
    정리필요2 2007. 10. 3. 20:06

    우선 C(혹은 C++)에서 사용하는 배열과 포인터에 대해서 잠시 언급을 드리자면....


    보통 C를 수업같은 걸 통해서 배우다보면 으례 '배열과 포인터는 같은거다' 라는 식으로 얘기를 하는 경우가 많이 있는데여......


    엄밀히 말해서 C에서는 '포인터를 이용해 배열을 효과적으로 접근(사용, 컨트롤)할 수 있다' 일 뿐이지..... '같은 것'은 아니라는 점입니다...


    이러한 경우가 바로 그런 점을 단적으로 보여주는 구체적인 사례라고 생각할 수 있겠네요.....


    님께서 말씀하신 것처럼 1차원 배열의 경우에는 위처럼 배열의 주소값을 넘겨줘서 함수에서 그 주소를 받아 접근(by reference)이 가능하죠...


    근데 이게 2차원 배열을 넘기면 컨버팅 에러가 발생합니다....


    왜 일까요.......???


    배열과 포인터의 생리에 대한 이해가 어느 정도 정립되기 이전에 이런 상황에서 좀 곤혹을 겪는 것 같습니다만.....


    언뜻 직관적으로 생각하기에는 1차원 배열의 주소값을 넘겨줘서 1차원 배열의 포인터로 그걸 받았으니까.... 2차원 배열의 주소를 넘겨주면 2차 포인터로 받아서 쓰면 되겠지... 라고 생각이 되죠.....


    그러나 여기서는 1차원과 달리 2차원 배열의 문제가 생기는 것이.....


    본래 배열이란 게 메모리 내에서 일련된 다수의 공간을 확보하고 사용하는 방법이죠....


    메모리상에서 배열은 우리가 인식하는 것처럼 가로 * 세로로 된 형태가 아니라는 건 아실겁니다... 실제로는 그냥 1차원 배열과 같은 선형(linear) 배열일 뿐입니다......


    그걸 2차원 배열로 확보를 하게 되면 [] 연산자의 처리 방법을 컴파일러가 정해서 적당히 처리하는 것이죠......


    예를 들어 array[100][100] 이 있을 때....


    특정 위치의 배열요소 array[i][j] 라는 것은 실제 array라는 주소값으로부터 100 * i + j 만큼 떨어져있는 것이 됩니다.....


    좀 더 구체적으로 예를 들자면....


    #define WIDTH 10

    #define HEIGHT 5


    int array[HEIGHT][WIDTH];

    int *pArray = (int*)array;

    int i, j;


    scanf("%d %d", &i, &j);

    printf("%d / %d\n", array[i][j], pArray[i * WIDTH + j]);


    이렇게 하면 실질적으로 같은 배열 요소의 값을 두번 출력하는 상황이 된다는 겁니다..


    여기서 생각해야 할 것이 무엇인가하면... 결국 2차원 배열이라는 것은 논리적인 사용방법일 뿐 실제 내부적으로 처리가 되는 것은 1차원 형태라는 것이죠.....


    바꿔 말해서 2차원 배열에서 특정 위치를 접근하기 위해서(내부적으로는 1차원 배열인데) 필요한 것은 배열의 가로 크기 라는 점입니다.....


    배열이 가로를 몇으로 정해놓은 배열인지를 알아야 array[i][j]가 실제 선형배열 상에서 어디쯤 위치하는지 알 수가 있다는 것이죠.....


    앞으로 다시 돌아가서 1차원 배열은 처리가 되는데 2차원 배열은 처리가 안되더라.. 라고 했던 말을 생각해봤을 때... 함수에 2차원 배열의 주소값을 넘겨주게 되면... 그건 어디까지나 배열의 시작 번지만을 전해줬을 뿐인 겁니다....


    함수 입장에서 그 주소값을 받았을 때... 이 쪽에서는 실제 위치를 알 수 없는 상황이 되는거죠....


    결국 2차원 포인터로 2차원 배열을 컨트롤 할 수는 없는 형태가 되기 때문에 위와 같은 경우 에러가 발생하는 거구요.....


    그럼 방법이 없는가...... 그건 아니겠죠...... ^^ㅋ


    일단 언뜻 2가지의 방법이 떠오르는데요.... (뭐 다른 방법이 더 있을 수 있겠지만...)


    첫번째 위처럼 배열을 애시당초 정적인 형태로 할당을 했을 경우에는


    함수에서 넘겨줄 때는 1차원 포인터로 넘겨주는 방법이죠......


    즉...

    int main(void) {

        int array[HEIGHT][WIDTH];

        funcTest((int*)array);

    }


    void funcTest(int *pArray) {

       // 특정 요소 array[i][j] 를 접근하기 위해

       int value = pArray[i * WIDTH + j];

    }


    이런 방법이 있겠구요......




    두번째로 굳이 함수 파라미터로 2차원 포인터를 넘겨서 함수 내에서도 array[i][j] 같은 형태로 사용을 해야겠다면.....


    애시당초 배열을 동적으로 생성을 하는 방법이 있습니다....


    int main(void) {
        // 배열을 동적으로 생성
        int **array = (int**)malloc(HEIGHT * sizeof(int*));


        array[0] = (int*)malloc(WIDTH * HEIGHT * sizeof(int));
        for(int i = 1; i < HEIGHT; i++) array[i] = array[i - 1] + WIDTH;


        funcTest(array);


        // 사용후에는 반드시 소거
        free(array[0]);
        free(array);
    }


    void funcTest(int **pArray) {
        int value = pArray[i][j];
    }


    이 방법의 경우는 인위적으로 2차원 배열을 만든다고 할 수 있는 방법인데......


    [][] 연산자를 논리적으로 사용가능케끔 만들어진 형태라고 볼 수 있습니다....


    여기서는 2차원 형태의 배열을 넘겨줘서 2차 포인터로 받는 것이 가능하네... 라는 느낌이 들겠지만... 뭐 자세한 것은 배열을 어떻게 구성시켰는가... 라는 점을 소스를 찬찬히 들여다보면... 왜 가능한지 이해가 되리라고 보여집니다......




    배열에 대한 개념이 좀 더 확실히 정립이 되기를 바라면서.....


    그럼 이만.... :)

Designed by Tistory.