extern "C" 키워드는 C++에서 함수 이름이 C 언어 방식의 링키지(linkage)를 갖도록 만들 때 사용합니다. 이 경우 컴파일러는 함수 이름을 맹글링(mangling)하지 않습니다. extern "C"를 제대로 이해하려면 먼저 C++의 네임 맹글링이 무엇인지 살펴봐야 합니다.
C++ 네임 맹글링(Name Mangling)이란?
C++에는 함수 오버로딩(function overloading)이라는 강력한 기능이 있습니다. 이 기능을 활용하면 이름이 같은 함수를 여러 개 만들 수 있으며, 매개변수의 타입과 개수만 다르면 됩니다(반환 타입은 구분 기준에 포함되지 않습니다). 그렇다면 컴파일러는 오브젝트 코드에서 어떻게 오버로드된 함수들을 서로 구별할까요?
컴파일러는 오브젝트 코드를 생성할 때 매개변수에 대한 정보를 함수 이름에 추가하여 이를 구분합니다. 이 기법을 바로 네임 맹글링(Name Mangling)이라고 부릅니다. 흥미롭게도 C++ 표준은 네임 맹글링 방식을 규정하지 않기 때문에, 컴파일러마다 서로 다른 기법을 사용합니다.
네임 맹글링 예시
다음처럼 int형과 double형으로 오버로딩된 func() 함수와 my_function() 함수가 있다고 가정해 보겠습니다.
int func(int x) {
return x*x;
}
double func(double x) {
return x*x;
}
void my_function(void) {
int x = func(2); // 정수 버전 호출
double y = func(2.58); // 실수 버전 호출
}
일부 C++ 컴파일러는 위 코드를 다음과 같이 변경합니다.
int __func_i(int x){
return x*x;
}
double __func_d(double x){
return x*x;
}
void __my_function_v(void){
int x = __func_i(2); // 정수 버전
double y = __func_d(2.58); // 실수 버전
}
함수 이름 뒤에 매개변수 타입 정보(i는 int, d는 double, v는 void)가 붙어 서로 다른 심볼로 구분되는 것을 확인할 수 있습니다.
C 코드와 링크할 때 발생하는 문제
C 언어는 함수 오버로딩을 지원하지 않습니다. 따라서 C++ 코드에서 C 코드를 링크할 때는 심볼 이름이 변경되지 않도록 보장해야 합니다. 그렇지 않으면 아래와 같은 코드에서 오류가 발생합니다.
int printf(const char *format,...);
main() {
printf("Hello World");
}
출력 결과
undefined reference to `printf(char const*, ...)'
ld returned 1 exit status
이 문제는 컴파일러가 printf()의 이름을 맹글링하기 때문에 발생합니다. 링커는 변경된 이름의 printf() 함수 정의를 찾지 못하고, 결국 'undefined reference' 오류를 출력하게 됩니다.
extern "C"로 문제 해결하기
이 문제를 해결하려면 C++에서 extern "C" 블록을 사용해야 합니다. 이 블록 안에 선언된 코드는 C++ 컴파일러가 함수 이름을 맹글링하지 않도록 보장하므로, 이름이 C 방식 그대로 유지됩니다. 앞선 코드는 다음과 같이 수정하면 정상적으로 동작합니다.
extern "C"{
int printf(const char *format,...);
}
main() {
printf("Hello World");
}
출력 결과
Hello World
참고: 위 코드 블록들은 사용하는 컴파일러에 따라 다른 결과를 낼 수 있습니다.
정리: extern "C" 사용 시 주의사항
결국 extern "C"는 일종의 링키지 지정자(linkage specification)입니다. 모든 컴파일러에서 C 링키지를 지정하려면 이 키워드를 사용해야 하며, 다음 두 가지 사항도 함께 기억해야 합니다.
- 링키지 지정자는 반드시 네임스페이스 범위(namespace scope)에 위치해야 합니다.
- 클래스 멤버 함수는 extern "C"의 영향을 받지 않고 무시됩니다.