Computer >> 컴퓨터 >  >> 프로그래밍 >> C 프로그래밍

C++ 이름 맹글링(Name Mangling)과 extern "C" 완벽 이해하기

C++ 함수 오버로딩과 컴파일러의 처리 방식

C++는 함수 오버로딩(Function Overloading) 기능을 제공합니다. 이 기능을 활용하면 같은 이름을 가진 여러 개의 함수를 정의할 수 있으며, 각 함수는 매개변수의 타입이나 개수만 다르면 됩니다. 단, 반환 타입은 오버로딩 구분에 사용되지 않습니다.

그렇다면 C++ 컴파일러는 오브젝트 코드(Object Code) 수준에서 이렇게 동일한 이름을 가진 오버로딩된 함수들을 어떻게 구분할까요?

비밀은 바로 이름 맹글링(Name Mangling)이라는 기법에 있습니다. 컴파일러는 오브젝트 코드를 생성할 때 함수 이름 뒤에 매개변수 정보를 추가하여 실제 내부 심볼(symbol) 이름을 변경합니다. 흥미로운 점은 C++ 표준이 이름 맹글링 방식을 규정하지 않기 때문에, 컴파일러마다 서로 다른 맹글링 기법을 사용한다는 것입니다.

이름 맹글링 예제

아래 코드에서는 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); // double 버전 호출
}

특정 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); // double
}

함수 이름 뒤에 붙은 _i, _d, _v처럼 매개변수 타입 정보가 이름에 반영되어, 컴파일러와 링커가 오버로딩된 함수들을 구별할 수 있게 됩니다.

C 코드와 연결 시 발생하는 문제

C 언어는 함수 오버로딩을 지원하지 않습니다. 따라서 C++ 코드에서 C로 작성된 코드를 링크할 때는 심볼 이름이 변경되지 않도록 보장해야 합니다. 그렇지 않으면 아래 코드처럼 에러가 발생합니다.

int printf(const char *format,...);
main() {
    printf("Hello World");
}

출력

undefined reference to `printf(char const*, ...)'
ld returned 1 exit status

문제의 원인

C++ 컴파일러가 printf()의 이름을 자체적으로 맹글링해버렸기 때문에, 링커가 실제 C 라이브러리에 존재하는 printf()의 정의를 찾지 못하고 'undefined reference' 에러를 발생시키는 것입니다.

extern "C"로 문제 해결하기

이 문제를 해결하려면 C++에서 제공하는 extern "C" 선언을 사용해야 합니다. extern "C" 블록 안에 포함된 코드는 C++ 컴파일러가 맹글링하지 않고 원래의 C 스타일 이름 그대로 유지됩니다. 즉, 함수 이름이 변경되지 않으므로 C 라이브러리와의 호환성이 확보됩니다.

위의 코드를 extern "C"를 사용해 수정하면 다음과 같습니다.

extern "C" {
    int printf(const char *format,...);
}
main() {
    printf("Hello World");
}

출력

Hello World

extern "C" 블록 덕분에 printf()라는 이름이 그대로 유지되어, C 표준 라이브러리의 printf() 정상적으로 연결되었음을 확인할 수 있습니다.

참고 사항

주의: 이름 맹글링 방식은 표준화되어 있지 않기 때문에, 동일한 코드라도 사용하는 컴파일러나 플랫폼에 따라 결과가 달라질 수 있습니다.