이전 시간에 모듈화 프로그래밍(소스 나누기)를 할 때 다른 소스에 있는 배열 변수를 포인터 변수로 사용하면 안 된다는 말씀을 올렸습니다.


void print_fun( char *str){

    printf( "str=%s\n", str);

}


int main( void){

    char    str[]   = "falinux.com";


    print_fun( str);

    return 0;

}


이런 코딩이 가능하기 때문에 소스 나누기를 해도 다른 소스의 배열 변수를 포인터 변수로 받을 수 있는 것처럼 생각되어서 C언어는 아차 실수하기 쉽습니다.


main.c 에 아래와 같은 소스가 있습니다.


extern void print_fun( void);


char    str[]   = "falinux.com";


int main( void){

    print_fun();

    return 0;

}


main.c에서 호출하는 print_fun()를 sub.c에서 구현했다고 하겠습니다. sub.c에서 아래와 같이 작성되면 컴파일은 문제 없어도 실행할 때 에러가 발생합니다.


extern char *str;

void print_fun( void){

    printf( "str=%s\n", str);

}


아래와 같이 배열로 선언해 주어야 에러가 발생하지 않습니다.


extern char str[];


void print_fun( void){

    printf( "str=%s\n", str);

}


왜 이런 문제가 발생할 까요? 이는 C언어에서 배열과 포인터는 사용하는 방법이 비슷해도 메모리에 생성되는 방식이 전혀 다르기 때문입니다. 포인터 변수는 포인터 변수 자체에 대한 메모리 할달이 됩니다.


즉,


char  *ptr;


이라고 선언하면 ptr 이라는 변수에 주소 값을 넣을 수 있는 메모리가 할당 되지요.


그러나


char ary[10];


이렇게 배열을 선언하면 ary[0], ary[1], ary[2]는 메모리에 할당되어도 ary 는 존재하지 않습니다.


그러므로 C언어에서 컴파일을 소스별로 개별적으로 하면 C언어는 sub.c에 있는 extern char *str; 에서 str을 위한 포인터 변수를 선언해 놓습니다. 여기까지는 자연스럽습니다.

문제는 링크 과정입니다. 컴파일러가 sub.c의 str를 포인터 변수로 할달해 놓았는데 링커가 main.c에 있는 str 변수를 포인터로 생각하고 넣으려고 합니다. 왜냐하면 extern char *str; 로 선언해 놓았기 때문이죠.

하지만, 실제로 main.c의 str 변수는 배열입니다. 링커가 친절하게 str[0]의 주소를 넣어 주면 되겠지만, 이미 컴파일러가 외부 변수 str은 포인터라고 알려 주었기 때문에 링커는 main.c의 str을 배열로 생각하지를 않습니다. 그러므로 존재하지도 않는 배열 변수 str에 대한 주소값을 넣으려니 당연히 엉뚱한 값을 넣게 되고 컴파일과 링크까지 이상이 없어도 실행하면 에러가 발생하게 됩니다.