C 와 C++ 프로그램 링크(link) 방법 [두번째] : C++ 에서 C 의 함수 호출방법
지난 시간에 mangling/demangling 에 대해 알아보았습니다.
자... 이번 시간에는 먼저 c++ 에서 c 의 함수를 호출하는 방법을 알아보도록 하죠.
먼저 지난 시간에 사용한 코드를 gcc 로 컴파일해둡니다.
sh> cat test_func.c #include <stdio.h> void test_func(const char * s) { printf("[%s] : [%s]n", __func__, s); } sh> gcc -c test_func.c
그런 다음 이를 실제로 호출하는 cpp 프로그램 파일을 만들어보죠.
sh> g++ -c call.cpp void test_func(const char * s); int main() { test_func("foon"); return 0; }
이제 두개의 object 를 링크하여 call 이라는 실행파일을 만들어봅니다.
sh> g++ -o call test_func.o call.o call.o: In function `main': call.cpp:(.text+0xa): undefined reference to `test_func(char const*)' collect2: ld returned 1 exit status
그런데, 이게 웬일일까요 ? 분명히 test_func.c 에 test_func() 함수를 구현하고 object 까지 만들어뒀는데 해당 함수가 없다고 합니다.
이유는 간단합니다. 지난 시간에 알려드렸던 mangling 을 정확히 이해하고 계셨다면 금방 답을 짐작하셨을 수도 있을겁니다.
컴퓨터는 거짓말을 안합니다. ㅎㅎ ^^
sh> nm test_func.o 000000000000000d r __func__.2044 U printf 0000000000000000 T test_func sh> nm call.o U _Z9test_funcPKc 0000000000000000 T main
위의 nm 결과(symbol table list)를 보세요.
test_func.o 의 symbol table 에는 test_func 라는 이름으로 등록이 되어 있습니다.
그런데, 호출하는 측의 call.o 파일에는 mangling 된 이름으로 등록이 되어 있습니다.
그리고, U 라고 표시되어 있죠. (이건 지난 시간에 다른 object 나 library 에 포함된 symbol 이라고 말씀드렸죠.)
즉, call.o 입장에서는 다른 object 나 dynamic library 파일에 _Z9test_funcPKc 라는 이름으로 등록된 함수를 호출하려고 한다는 의미입니다.
위의 nm 결과를 보면 이런 이름의 함수가 어느 object 에도 없으니 당연히 실패가 되는겁니다.
그럼 이를 어찌해야 하나요 ? 함수이름을 그렇게 지어야 하나 ??? ㅎㅎㅎ
그래서, 등장하는 것이 여러분이 그동안 종종 보아왔을지도 모르는 extern "C" 라는 키워드입니다.
아래의 결과를 한번 위와 비교해보도록 하죠.
sh> cat call.cpp extern "C" void test_func(const char * s); # extern "C" 추가 int main() { test_func("foon"); return 0; } sh> g++ -c call.cpp # 다시 컴파일 sh> g++ -o call test_func.o call.o # 링크 sh> nm call.o 0000000000000000 T main U test_func # mangling 이 안되어 있음.
코드에 extern "C" 를 추가했더니 링크 시에 발생하던 오류가 사라졌고, call.o 파일의 nm 결과로 나오는 symbol table 에서 test_func 함수의 이름도 mangling 이 안된 상태로 나오는군요.
그러니, call.o 파일에서 test_func.o 파일에 있는 test_func() 함수를 호출하는게 가능하겠네요.
흠...
이의 결과를 보면 extern "C" 의 효과를 짐작해볼 수 있습니다.
extern "C" 는 c++ 컴파일러로 컴파일을 할 때 함수의 인자 등을 고려하지 않고, 즉 mangling 하지 않고 마치 C 함수처럼 취급하여 symbol table 에 남기도록 하기 위한 것입니다.
즉, 이는 컴파일러에서 자료형을 검사하는 안전성 수준을 C 수준으로 떨어뜨리는 것입니다.
간단하게 말하면 C 코드와 C++ 코드 간에 호출을 자유롭게 하려면 당연히 symbol 이름을 같게 만드는 무언가를 적용해야한다는 원리인거죠.
아마 여러분은 아래와 같은 코드를 header 파일에서 수없이 보셨을 겁니다. 이것이 모두 위와 같은 이유로 C 의 헤더파일을 C++ 에서도 문제없이 사용할 수 있도록 미연에 방지하기 위해 넣어두는 것입니다.
#ifdef __cplusplus extern "C" { #endif void test_func(const char * s); #ifdef __cplusplus } #endif