[c++] C 함수는 왜 이름을 바꿀 수 없습니까?

최근에 인터뷰를 extern "C"했고 C ++ 코드에서 무엇이 사용되는지에 대한 질문이있었습니다 . C는 이름 맹 글링을 사용하지 않기 때문에 C ++ 코드에서 C 함수를 사용하는 것이라고 대답했습니다. 나는 왜 C가 이름 맹 글링을 사용하지 않고 정직하게 대답 할 수 없는지 물었다.

C ++ 컴파일러가 함수를 컴파일 할 때 C ++에서 같은 이름의 오버로드 된 함수를 컴파일 타임에 해결해야하기 때문에 함수에 특별한 이름을 부여한다는 것을 이해합니다. C에서 함수 이름은 동일하게 유지되거나 앞에 _가 붙습니다.

내 쿼리는 : C ++ 컴파일러가 C 함수를 맹 글링하도록 허용하는 데 어떤 문제가 있습니까? 컴파일러가 어떤 이름을 제공하는지는 중요하지 않다고 가정했을 것입니다. C와 C ++에서 같은 방식으로 함수를 호출합니다.



답변

그것은 위에서 대답했지만 일종의 맥락에 넣을 것입니다.

먼저 C가 먼저 나왔습니다. 따라서 C가하는 것은 일종의 “기본”입니다. 이름이 틀리기 때문에 이름을 맹 글링하지 않습니다. 기능 명은 기능 명입니다. 글로벌은 글로벌 등입니다.

그런 다음 C ++가 등장했습니다. C ++은 C와 동일한 링커를 사용하고 C로 작성된 코드와 링크 할 수 있기를 원했지만 C ++은 C를 “맨 글링 (mangling)”상태로 남겨 둘 수 없었습니다. 다음 예를 확인하십시오.

int function(int a);
int function();

C ++에서 이들은 고유 한 기능을 가진 고유 한 기능입니다. 이들 중 어느 것도 엉망이 아닌 경우 둘 다 “함수”(또는 “_ 함수”)라고하며 링커는 심볼의 재정의에 대해 불평합니다. C ++ 솔루션은 인수 유형을 함수 이름으로 변환했습니다. 따라서 하나는 호출 _function_int되고 다른 하나는 호출됩니다 _function_void(실제 mangling 구성표는 아님).

이제 문제가 생겼습니다. 경우 int function(int a)는 C 모듈에 정의 된, 우리는 단지 코드를 C ++에서 헤더 (즉, 선언)을 복용하고 그것을 사용하고, 컴파일러는 수입에 링커 명령을 생성합니다 _function_int. 함수가 정의되었을 때 C 모듈에서는 호출되지 않았습니다. 호출되었다 _function. 링커 오류가 발생합니다.

이 오류를 피하기 위해 함수를 선언 하는 동안 컴파일러에게 C 컴파일러와 연결되거나 C 컴파일러에 의해 컴파일되도록 설계된 함수라고 알려줍니다.

extern "C" int function(int a);

C ++ 컴파일러는 이제가 _function아닌 가져 오기를 알고 _function_int있으며 모든 것이 좋습니다.


답변

그들이 “할 수 없다” 는 것은 아니며 , 일반적으로 그렇지 않습니다 .

C 라이브러리에서이라는 함수를 호출하려면 foo(int x, const char *y)C ++ 컴파일러가 foo_I_cCP()할 수 있기 때문에 C ++ 컴파일러 에서 맹 글링 체계를 구성하는 것이 좋지 않습니다 .

이 이름은 해결되지 않고 함수는 C에 있으며 이름은 인수 유형 목록에 의존하지 않습니다. 따라서 C ++ 컴파일러는 이것을 알아야하고 그 기능을 C로 표시하여 조작을 피합니다.

C 함수는 소스 코드가없는 라이브러리에있을 수 있으며, 미리 컴파일 된 바이너리와 헤더 만 있으면됩니다. 따라서 C ++ 컴파일러는 “자신의 일”을 수행 할 수 없으며 결국 라이브러리의 내용을 변경할 수 없습니다.


답변

C ++ 컴파일러가 C 함수를 맹 글링하는 것이 무엇이 잘못 되었습니까?

더 이상 C 함수가 아닙니다.

함수는 단순한 서명과 정의가 아닙니다. 함수의 작동 방식은 주로 호출 규칙과 같은 요소에 의해 결정됩니다. 플랫폼에서 사용하도록 지정된 “응용 프로그램 이진 인터페이스”는 시스템이 서로 통신하는 방법을 설명합니다. 시스템에서 사용중인 C ++ ABI는 이름 관리 체계를 지정하여 해당 시스템의 프로그램이 라이브러리 등에서 함수를 호출하는 방법을 알 수 있도록합니다. (예를 들어 C ++ Itanium ABI를 읽으십시오. 왜 필요한지 매우 빨리 알 수 있습니다.)

시스템의 C ABI에도 동일하게 적용됩니다. 일부 C ABI에는 실제로 이름 관리 체계 (예 : Visual Studio)가 있으므로 특정 기능의 경우 “이름 관리 해제”와 C ++ ABI에서 C ABI 로의 전환에 관한 것이 아닙니다. C 함수를 C 함수로 표시하고 C ++ ABI가 아닌 C ABI가 관련이 있습니다. 선언은 정의와 일치해야합니다 (동일한 프로젝트 또는 일부 타사 라이브러리에서). 그렇지 않으면 선언이 의미가 없습니다. 그렇지 않으면 시스템은 단순히 해당 기능을 찾고 호출하는 방법을 알지 못합니다.

플랫폼이 C 및 C ++ ABI를 동일하게 정의하지 않고이 “문제”를 제거하는 이유는 부분적으로 역사적입니다. 원래 C ABI는 네임 스페이스, 클래스 및 연산자 오버로드가있는 C ++에는 충분하지 않았습니다. 그 중 일부는 컴퓨터 친화적 인 방식으로 심볼 이름으로 표시되어야하지만 C 커뮤니티에서 C 프로그램을 준수하도록 만드는 것은 C 커뮤니티에서 불공평하다고 주장 할 수 있습니다. ABI는 단지 상호 운용성을 원하는 다른 사람들을 위해서입니다.


답변

사실 MSVC는 않습니다 간단한 방식으로하지만, 압착 롤러 C 이름을. 때때로 @4또는 다른 작은 숫자를 추가 합니다. 이것은 호출 규칙 및 스택 정리의 필요성과 관련이 있습니다.

따라서 전제는 결함이 있습니다.


답변

부분적으로 C로 작성되고 일부 다른 언어 (종종 어셈블리 언어이지만 때로는 파스칼, FORTRAN 또는 기타)로 작성된 프로그램을 갖는 것이 매우 일반적입니다. 프로그램에 모든 소스 코드가없는 다른 사람들이 작성한 다른 구성 요소가 포함되어있는 것도 일반적입니다.

대부분의 플랫폼에는 특정 유형의 인수를 허용하고 특정 유형의 값을 반환하는 특정 이름을 가진 함수를 생성하기 위해 컴파일러가 수행해야하는 작업을 설명하는 ABI [Application Binary Interface]라는 사양이 있습니다. 경우에 따라 ABI는 둘 이상의 “호출 규칙”을 정의 할 수 있습니다. 이러한 시스템의 컴파일러는 특정 함수에 어떤 호출 규칙을 사용해야하는지 나타내는 수단을 제공합니다. 예를 들어, Macintosh에서는 대부분의 Toolbox 루틴이 Pascal 호출 규칙을 사용하므로 “LineTo”와 같은 프로토 타입은 다음과 같습니다.

/* Note that there are no underscores before the "pascal" keyword because
   the Toolbox was written in the early 1980s, before the Standard and its
   underscore convention were published */
pascal void LineTo(short x, short y);

프로젝트의 모든 코드가 동일한 컴파일러를 사용하여 컴파일 된 경우 컴파일러가 각 함수에 대해 내 보낸 이름은 중요하지 않지만 많은 상황에서 C 코드가 다른 도구를 사용하여 컴파일 된 함수를 호출해야합니다. 현재 컴파일러로 다시 컴파일 할 수 없으며 C에도 없을 수도 있습니다. 따라서 링커 이름을 정의 할 수있는 기능은 이러한 기능을 사용하는 데 중요합니다.


답변

다른 접선 토론을 해결하기 위해 다른 답변 하나를 추가하겠습니다.

C ABI (응용 프로그램 이진 인터페이스)는 원래 스택에서 인수를 역순으로 (즉, 오른쪽에서 왼쪽으로 푸시) 전달해야했으며 호출자도 스택 저장소를 비 웁니다. 현대 ABI는 실제로 인수를 전달하기 위해 레지스터를 사용하지만 많은 고민 고려 사항은 원래 스택 인수 전달로 되돌아갑니다.

대조적으로, 원래 파스칼 ABI는 논쟁을 왼쪽에서 오른쪽으로 밀었 고, 수신자는 논쟁을 터뜨려 야했다. 원래 C ABI는 두 가지 중요한 점에서 원래 Pascal ABI보다 우수합니다. 인수 푸시 순서는 첫 번째 인수의 스택 오프셋이 항상 알려져 있으므로 알 수없는 인수 수를 갖는 함수를 허용합니다. 여기서 초기 인수는 다른 인수 수 (ala printf)를 제어합니다 .

C ABI가 우월한 두 번째 방법은 발신자와 수신자가 몇 개의 인수에 동의하지 않는 경우의 동작입니다. C의 경우 실제로 마지막 인수를 지난 인수에 액세스하지 않는 한 나쁜 일이 없습니다. Pascal에서 스택에서 잘못된 수의 인수가 표시되고 전체 스택이 손상되었습니다.

원래 Windows 3.1 ABI는 Pascal을 기반으로했습니다. 따라서 Pascal ABI (왼쪽에서 오른쪽 순서의 인수, 수신자 수신자)가 사용되었습니다. 인수 번호가 일치하지 않으면 스택이 손상 될 수 있으므로 맹 글링 구성표가 형성되었습니다. 각 함수 이름은 인수의 크기를 바이트 단위로 나타내는 숫자로 엉망입니다. 따라서 16 비트 시스템에서 다음 함수 (C 구문)는 다음과 같습니다.

int function(int a)

너비가 2 바이트 function@2이므로으로 맹 글링되었습니다 int. 선언과 정의가 일치하지 않으면 링커가 런타임에 스택을 손상시키지 않고 함수를 찾지 못합니다. 반대로, 프로그램이 연결되면 호출이 끝날 때 스택에서 올바른 바이트 수가 팝되는지 확인할 수 있습니다.

32 비트 Windows 이상에서는 stdcallABI를 대신 사용하십시오 . 푸시 순서는 C에서와 마찬가지로 오른쪽에서 왼쪽으로 파스칼 ABI와 유사합니다. Pascal ABI와 마찬가지로 이름 맹 글링은 스택 손상을 피하기 위해 인수 바이트 크기를 함수 이름으로 엉망으로 만듭니다.

여기 다른 곳에서 만든 클레임과 달리 C ABI는 Visual Studio에서도 함수 이름을 엉망으로 만들지 않습니다. 반대로 stdcallABI 사양으로 장식 된 맹 글링 기능 은 VS 고유하지 않습니다. GCC는 Linux를 컴파일 할 때도이 ABI를 지원합니다. 이것은 Wine 에 의해 광범위하게 사용되며 , 자체 컴파일 된 로더를 사용하여 Linux 컴파일 된 바이너리를 Windows 컴파일 된 DLL에 런타임 링크 할 수 있습니다.


답변

C ++ 컴파일러는 서명이 다른 오버로드 된 함수에 고유 한 심볼 이름을 허용하기 위해 이름 맹 글링을 사용합니다. 기본적으로 인수 유형을 인코딩하여 함수 기반 수준에서 다형성을 허용합니다.

C는 함수의 과부하를 허용하지 않기 때문에 이것을 요구하지 않습니다.

이름 맹 글링은 ‘C ++ ABI’에 의존 할 수없는 이유 중 하나 일뿐입니다.