별빛의 낙하지 :: [C++] C++ name decoration 혹은 name mangling

[C++] C++ name decoration 혹은 name mangling

C++ 2012. 11. 13. 19:48 Posted by byulbit

C++은 함수 오버로딩을 지원하기 때문에 기존의 C스타일의 이름의 함수로는 함수오버로딩을 지원할 수 없다. 

따라서 함수 이름에 대한 데코레이션(decoration) 혹은 맹글링(mangling)이 필요하다.

(데코레이션과 맹글링은 이름을 꾸미는 것, 섞는 것이라는 그런 의미다.)

  

*네임 맹글링 자체가 함수를 구분해주기 위한 용도로써라고 이해하기 되면 약간 거시기 하다.

정확히 이야기하면 C에서도 함수를 부를때 네임 맹글링을 한다. 

"_"를 함수 이름에 붙이는데,  C++기준으로 보면 이건 네임맹글링 수준이 매우 미미하다.


여기서는 함수 오버로딩을 지원하기 위한 측면에서 살펴보겠다.

name mangling, 혹은 name decoration을 하기 위한 것은 컴파일러가 결정하게 되는데,

일반적으로 함수의 파라미터로 함수 지원한다고 생각했었으나 실제 테스트 해보니 그렇지 않았다.

VC++ Compiler에서 반환값도 네임 맹글링에 포함된다.

예를 들어서 아래는 반환값 test2(void) 의 함수 시그니처이다.

 

int __cdecl test2(void)" (?test2@@YAHXZ) -H(int return)

void __cdecl test2(void)" (?test2@@YAXXZ) -X(void return)

float __cdecl test2(void)" (?test2@@YAMXZ) -M(float return)

char __cdecl test2(void)" (?test2@@YADXZ) -D(char return)


보다시피 반환값이 바뀌게 되면 함수의 시그니처 또한 바뀐다.

약간 재밌는 것은 VS에서 C++에서 함수를 호출하는데, 함수의 원형 선언이 없으면 아래와 같은 에러를 
ClCompile 단계에서 낸다. ( C에서는 함수의 선언과 관계가 없이  정의가 존재하면, 함수의 선언과 여부없이 에러를 내지 않는다.)

error C3861: 'test2': identifier not found

위의 에러는 test2 함수를 선언하지 않은 경우로, test2에 대한 identifier 가 없다는 에러인데, 컴파일 단계, 즉 번역 단계에서 에러가 나는 것이라고 추론할 수 있다. 

  또한 C++에서는 이름이 같은 함수끼리의 함수 오버로딩 집합이 존재하며, 오버로딩 집합을 통해서 함수를 부를때 호출하는 측의 시그니처가 동일하지 않아도 오버로딩 해석에 따라서 함수를 부를 수가 있다.

함수에 대한 다른 오버로드한 함수가 존재해서 부르는 함수의 파라미터 개수가 맞는 것이 없다면 아래와 같은 오률르 낸다.
error C2661: 'test2' : no overloaded function takes 3 arguments


함수가 선언이 존재하고, 해당 함수의 정의가 존재하지 않으면 외부 심볼을 찾을 수 없다고 링커 오류를 낸다.
error LNK2019: unresolved external symbol "int __cdecl test3(void) )

#include <stdio.h>


void test2();

int main()
{
//printf("%d", test2());
test2();

}


그렇다면 반환값만 다른 함수의 선언이 있다면 어떻게 될까?
이 경우 아래와 같은 에러를 낸다.
 error LNK2019: unresolved external symbol "void __cdecl test2(void)" (?test2@@YAXXZ) referenced in function _main

이것이 에러가 나는 지점은 함수의 선언이 존재하는데, 정확히 일치하는 해당하는 함수의 정의를 찾지 못했다는 의미다.

자 그러면 A라는 함수의 원형을 선언해놓고, 호출하는 측에서 B라는 함수를 호출, 다른 오브젝트 파일에 B 함수를 정의하면 어떻게 될까?

void test2의 정의를 넣어보았다.

// test2.cpp
int test2()
{
printf("dfdfdf");
return 0;
}

void test2()
{
printf("111111");
}


그러자 이번에는 아래와 같은 에러 메시지가 나온다. 
error C2556: 'void test2(void)' : overloaded function differs only by return type from 'int test2(void)'
error C2371: 'test2' : redefinition; different basic types

즉 오버로딩 함수들은 return type이 다른 것은 인정하지 않는 다는 것을 알 수 있다.
그래서 매개변수가 있는 오버로드 함수를 추가해보았다.

int test2(int a, int b)
{
return a + b;
}

부르는 쪽에서 보았을 때 함수의 선언이 존재하지 않아서 아래와 같은 에러를 낸다.
error C2660: 'test2' : function does not take 2 arguments 

이것은 함수의 오버로딩 집합이 선언에 의해서 판별된다는 것을 의미한다.
C++은 함수의 원형과 관련된 존재가 필수적이라는 것을 알 수 있다.
C의 경우 함수의 심볼이 함수의 이름으로 한정된다.  
즉 여러 오브젝트 파일을 링크 할때, 함수의 이름이 같다면, 해당하는 함수를 추가하기가 힘들다.
아래와 같이 심볼이 중복 정의 되어 있다고 에러를 내기 때문이다.
 fatal error LNK1169: one or more multiply defined symbols found

그렇다면 C++은 어떠할까?
C++의 경우 함수원형의 선언이 필수적이지만, 시그니처가 일치한다면 해당하는 함수를 부를 수 있으며,
오버로딩을 이용하여 함수의 이름이 같지만, 매개변수가 다른 것들을 추가할 수 있다.
물론 이 경우도 함수를 사용할시에 사용할 함수의 선언이 필수적이다.
 

C에서는 부르는 쪽에서 해당 함수가 존재하지 않으면 당당히 링커에서 오류를 낸다. 
 error LNK2019: unresolved external symbol _test1
위의 에러를 보면 알겠지만, "_함수이름" 으로 C에서는 네임 맹글링을 하며, 
즉 C는 오버로딩이 지원이 안된다.
하지만 해당하는 심볼이 존재한다면 호출이 가능한데 예를 들어서 함수의 정의 자체가

int test(int a)
{
printf("test(int a)");
return 0;
}

이렇게 정의가 되어 있더라도, 해당하는 함수를 다른 매개변수로써 호출할 수 있다.
int main()
{
test("Dfdfdfdf",2);

}

위의 코드는 test(int a)를 호출한다. 이것이 가능한 이유는 함수를 호출 할때는 함수의 심볼을 보고 찾기 때문이다.  즉 C에서는 test(int a)나 test(const char*, int) 로 추정되는 것들이나 함수의 심볼 자체는 _test로 같은 것으로 여겨지게 된다.

보통 C++에서 함수의 매개변수와 반환값 등으로 네임 맹글링을 한다고 생각할 때, 그럼 C++에서는 어떻게 가변인자 함수를 만들고 부를까? 라는 의구심이 생길 수 있다.  왜냐면 함수의 심볼이 같아야 함수의 정의를 찾을 수 있고, 따라서 함수 호출이 가능하기 때문이다.
테스트 해본 결과 재밌는 사실은 매개변수의 갯수 뿐만 아니라, 가변인자 함수인 경우의 네임맹글링도 존재하는 것이다.

void sprintfs(char *Result, const char *Format,  ...  ); 

이런 함수가 존재할 경우에

sprintfs("%d %d %d %d %d", &a, &b, &c, &d, &e); 이렇게 함수를 호출하는 경우와
sprintfs("%d %d %d %d", &a, &b, &c, &d);  이런 경우에 대해서 가변 인자 함수가 아니라면 심볼이 일치 하지 않아서 함수를 찾을 수 없어야 한다.

확인해본 결과 가변인자 함수의 경우 시그니처는 sprintfs@@YAXPADPBDZZ 로 동일하며 부르는 쪽의 매개변수가 달라져도,  심볼은 같으므로, 호출이 가능하다.