답변:
C와 C ++는 표면적으로 비슷하지만 각각 매우 다른 코드 세트로 컴파일됩니다. C ++ 컴파일러에 헤더 파일을 포함 시키면 컴파일러가 C ++ 코드를 기대합니다. 그러나 C 헤더 인 경우 컴파일러는 헤더 파일에 포함 된 데이터가 특정 형식 (C ++ 'ABI'또는 'Application Binary Interface')으로 컴파일 될 것으로 예상하므로 링커가 질식합니다. C ++ 데이터를 C 데이터를 기대하는 함수에 전달하는 것이 좋습니다.
(실제로 딱딱한 C ++의 ABI는 일반적으로 함수 / 메서드의 이름을 '혼합'하므로 printf()
프로토 타입을 C 함수로 플래그하지 않고 호출 하면 C ++은 실제로 코드 호출 _Zprintf
과 추가로 불필요한 쓰레기를 생성합니다 . )
따라서 extern "C" {...}
ac 헤더를 포함 할 때 사용 하면 간단합니다. 그렇지 않으면 컴파일 된 코드가 일치하지 않아 링커가 질식합니다. 그러나 대부분의 헤더에는 extern
대부분의 시스템 C 헤더가 이미 C ++ 코드와 코드에 포함될 수 있다는 사실을 설명하기 때문에 필요하지 않습니다 extern
.
#ifdef __cplusplus extern "C" { #endif
포함됩니다 . 따라서 C ++ 파일에 포함되어 있으면 여전히 C 헤더로 취급됩니다.
extern "C"는 생성 된 오브젝트 파일의 기호 이름 지정 방법을 결정합니다. extern "C"없이 함수를 선언하면 객체 파일의 심볼 이름은 C ++ 이름 맹 글링을 사용합니다. 다음은 예입니다.
주어진 test.C는 다음과 같습니다.
void foo() { }
객체 파일에서 심볼을 컴파일하고 나열하면 다음이 제공됩니다.
$ g++ -c test.C
$ nm test.o
0000000000000000 T _Z3foov
U __gxx_personality_v0
foo 함수는 실제로 "_Z3foov"라고합니다. 이 문자열에는 무엇보다도 리턴 유형 및 매개 변수에 대한 유형 정보가 포함됩니다. 대신 test.C를 작성하면 다음과 같습니다.
extern "C" {
void foo() { }
}
그런 다음 기호를 컴파일하고 살펴보십시오.
$ g++ -c test.C
$ nm test.o
U __gxx_personality_v0
0000000000000000 T foo
C 연결을 얻습니다. 객체 파일에서 "foo"함수의 이름은 "foo"일 뿐이며, 이름 맹 글링에서 나오는 모든 멋진 유형 정보가 없습니다.
일반적으로 extern "C"{} 안에 헤더를 포함 시키면 코드와 함께 제공되는 코드가 C 컴파일러로 컴파일되었지만 C ++에서 호출하려는 경우입니다. 이렇게하면 헤더의 모든 선언이 C 연결을 사용한다고 컴파일러에 알립니다. 코드를 연결하면 .o 파일에 "_Z3fooblah"가 아니라 "foo"에 대한 참조가 포함되며, 이는 연결하려는 라이브러리의 모든 항목과 일치합니다.
대부분의 최신 라이브러리는 이러한 헤더를 보호하여 심볼이 올바른 링크로 선언되도록합니다. 예를 들어 많은 표준 헤더에서 찾을 수 있습니다.
#ifdef __cplusplus
extern "C" {
#endif
... declarations ...
#ifdef __cplusplus
}
#endif
이를 통해 C ++ 코드에 헤더가 포함되면 객체 파일의 기호가 C 라이브러리의 기호와 일치해야합니다. C 헤더가 오래되었고 이러한 가드가없는 경우 C 헤더 주위에 extern "C"{}를 넣어야합니다.
C ++에서는 이름을 공유하는 다른 엔티티를 가질 수 있습니다. 예를 들어 다음은 모두 foo 라는 함수 목록입니다 .
A::foo()
B::foo()
C::foo(int)
C::foo(std::string)
이들을 모두 구별하기 위해 C ++ 컴파일러는 이름 변환 또는 데코레이션이라는 프로세스에서 각각 고유 한 이름을 만듭니다. C 컴파일러는 이것을하지 않습니다. 또한 각 C ++ 컴파일러는 다른 방법으로이를 수행 할 수 있습니다.
extern "C"는 C ++ 컴파일러에게 중괄호 안의 코드에서 이름을 바꾸지 말라고 지시합니다. 이를 통해 C ++ 내에서 C 함수를 호출 할 수 있습니다.
C와 C ++에는 기호 이름에 대한 규칙이 다릅니다. 심볼은 링커가 컴파일러가 생성 한 하나의 객체 파일에서 "openBankAccount"함수에 대한 호출이 동일한 (또는 호환 가능한) 다른 소스 파일에서 생성 된 다른 객체 파일에서 "openBankAccount"라는 함수에 대한 참조임을 인식하는 방법입니다. 컴파일러. 이를 통해 하나 이상의 소스 파일로 프로그램을 만들 수 있으며, 이는 큰 프로젝트에서 작업 할 때 도움이됩니다.
C에서 규칙은 매우 간단하며 기호는 모두 단일 이름 공간에 있습니다. 따라서 정수 "양말"은 "양말"로 저장되고 count_socks 함수는 "count_socks"로 저장됩니다.
링커는이 간단한 심볼 이름 지정 규칙을 사용하여 C 및 C와 같은 다른 언어를 위해 작성되었습니다. 따라서 링커의 기호는 단순한 문자열입니다.
그러나 C ++에서 언어를 사용하면 네임 스페이스, 다형성 및 간단한 규칙과 충돌하는 다양한 것들을 가질 수 있습니다. "add"라는 6 개의 다형성 함수 모두 다른 기호를 가져야합니다. 그렇지 않으면 다른 개체 파일에서 잘못된 기호를 사용합니다. 이것은 심볼의 이름을 "mangling"(기술적 인 용어)로 수행합니다.
C ++ 코드를 C 라이브러리 또는 코드에 링크 할 때 C 라이브러리에 대한 헤더 파일과 같이 C로 작성된 모든 항목을 extern "C"해야 C ++ 컴파일러에게 이러한 심볼 이름이 엉망이되지 않도록 할 수 있습니다. 물론 C ++ 코드는 엉망이되어야합니다. 그렇지 않으면 작동하지 않습니다.
C ++ 파일에서 사용되는 C 컴파일러가 컴파일 한 파일에 상주하는 함수를 정의하는 헤더를 포함 할 때마다 extern "C"를 사용해야합니다. (많은 표준 C 라이브러리는 개발자에게 더 간단하게하기 위해 헤더에이 검사를 포함 할 수 있습니다)
예를 들어, util.c, util.h 및 main.cpp 파일이 3 개이고 .c 및 .cpp 파일이 모두 C ++ 컴파일러 (g ++, cc 등)로 컴파일 된 경우 실제로 필요하며 링커 오류가 발생할 수도 있습니다. 빌드 프로세스가 util.c에 일반 C 컴파일러를 사용하는 경우 util.h를 포함 할 때 extern "C"를 사용해야합니다.
C ++은 함수의 매개 변수를 이름으로 인코딩합니다. 이것이 함수 오버로딩이 작동하는 방식입니다. C 함수에서 발생하는 모든 것은 이름의 시작 부분에 밑줄 ( "_")을 추가하는 것입니다. extern "C"를 사용하지 않으면 링커는 함수의 실제 이름이 _DoSomething ()이거나 DoSomething () 일 때 DoSomething @@ int @ float ()라는 함수를 찾습니다.
extern "C"를 사용하면 C ++ 컴파일러가 C ++ 대신 C 명명 규칙을 따르는 함수를 찾아야한다고 C ++ 컴파일러에 지시하여 위의 문제를 해결합니다.
g++
무슨 일이 일어나고 있는지 확인하기 위해 생성 된 바이너리를 디 컴파일
extern
필요한 이유를 이해하려면 가장 좋은 방법은 예제를 사용하여 오브젝트 파일에서 진행중인 작업을 자세히 이해하는 것입니다.
main.cpp
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }
GCC 4.8 Linux ELF 출력으로 컴파일 :
g++ -c main.cpp
심볼 테이블을 디 컴파일하십시오.
readelf -s main.o
출력에는 다음이 포함됩니다.
Num: Value Size Type Bind Vis Ndx Name
8: 0000000000000000 6 FUNC GLOBAL DEFAULT 1 _Z1fv
9: 0000000000000006 6 FUNC GLOBAL DEFAULT 1 ef
10: 000000000000000c 16 FUNC GLOBAL DEFAULT 1 _Z1hv
11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND eg
해석
우리는 그것을 본다 :
ef
및 eg
코드에서와 동일한 이름을 가진 심볼에 저장된
다른 상징들은 엉망이되었습니다. 그것들을 풀자 :
$ c++filt _Z1fv
f()
$ c++filt _Z1hv
h()
$ c++filt _Z1gv
g()
결론 : 다음 두 심볼 유형 모두 엉망 이 되지 않았습니다 .
Ndx = UND
다른 객체 파일에서 링크 또는 런타임에 제공되도록 선언되었지만 정의되지 않음 ( )따라서 extern "C"
전화를 걸 때 둘 다 필요 합니다.
g++
에 의해 생산 unmangled 문자 기대gcc
g++
엉킴없는 심볼을 생성 gcc
하도록 지시extern C에서 작동하지 않는 것들
이름 변경이 필요한 C ++ 기능은 내부에서 작동하지 않습니다 extern C
.
extern "C" {
// Overloading.
// error: declaration of C function ‘void f(int)’ conflicts with
void f();
void f(int i);
// Templates.
// error: template with C linkage
template <class C> void f(C i) { }
}
C ++ 예제에서 실행 가능한 최소 C
완전성을 위해 그리고 거기에있는 새로운 내용을 위해, 또한보십시오 : C ++ 프로젝트에서 C 소스 파일을 사용하는 방법을 .
C ++에서 C를 호출하는 것은 매우 쉽습니다. 각 C 함수에는 가능한 하나의 엉킴이없는 기호 만 있으므로 추가 작업이 필요하지 않습니다.
main.cpp
#include <cassert>
#include "c.h"
int main() {
assert(f() == 1);
}
ch
#ifndef C_H
#define C_H
/* This ifdef allows the header to be used from both C and C++. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif
#endif
cc
#include "c.h"
int f(void) { return 1; }
운영:
g++ -c -o main.o -std=c++98 main.cpp
gcc -c -o c.o -std=c89 c.c
g++ -o main.out main.o c.o
./main.out
extern "C"
링크가 없으면 다음과 같이 실패합니다.
main.cpp:6: undefined reference to `f()'
때문에 g++
상하는 찾기 위해이 망가 f
, 어떤gcc
생산하지 않았다.
C 예제에서 실행 가능한 최소 C ++
C ++에서 호출하는 것은 조금 더 어렵습니다. 노출하려는 각 함수의 엉킴이 아닌 버전을 수동으로 만들어야합니다.
다음은 C ++ 함수 과부하를 C에 노출시키는 방법을 보여줍니다.
main.c
#include <assert.h>
#include "cpp.h"
int main(void) {
assert(f_int(1) == 2);
assert(f_float(1.0) == 3);
return 0;
}
cpp.h
#ifndef CPP_H
#define CPP_H
#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif
int f_int(int i);
int f_float(float i);
#ifdef __cplusplus
}
#endif
#endif
cpp.cpp
#include "cpp.h"
int f(int i) {
return i + 1;
}
int f(float i) {
return i + 2;
}
int f_int(int i) {
return f(i);
}
int f_float(float i) {
return f(i);
}
운영:
gcc -c -o main.o -std=c89 -Wextra main.c
g++ -c -o cpp.o -std=c++98 cpp.cpp
g++ -o main.out main.o cpp.o
./main.out
extern "C"
그것이 없으면 실패합니다 :
main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'
g++
맹 글링 된 기호가 생성 되었기 때문에gcc
찾을 수없는 입니다.
우분투에서 테스트 18.04.