etc./StackOverFlow

C++에서 extern "C"의 효과는 무엇입니까?

청렴결백한 만능 재주꾼 2021. 12. 26. 04:07
반응형

질문자 :Litherum


extern "C" 를 C++ 코드에 넣는 것은 정확히 무엇을 합니까?

예를 들어:

 extern "C" { void foo(); }


extern "C" 는 C++의 함수 이름에 C 연결(컴파일러는 이름을 맹글링하지 않음)을 만들어 클라이언트 C 코드가 함수 선언만 포함하는 C 호환 헤더 파일을 사용하여 함수에 연결할 수 있도록 합니다. 함수 정의는 클라이언트 C 링커가 C 이름을 사용하여 연결할 바이너리 형식(C++ 컴파일러로 컴파일됨)에 포함되어 있습니다.

C++에는 함수 이름의 오버로딩이 있고 C는 그렇지 않기 때문에 C++ 컴파일러는 함수 이름을 링크할 고유 ID로 사용할 수 없으므로 인수에 대한 정보를 추가하여 이름을 맹글링합니다. C에서 함수 이름을 오버로드할 수 없기 때문에 AC 컴파일러는 이름을 맹글링할 필요가 없습니다. 함수 extern "C" 연결이 있다고 명시하면 C++ 컴파일러는 인수/매개변수 유형 정보를 사용된 이름에 추가하지 않습니다. 결합.

알다시피, extern "C" 연결을 명시적으로 지정하거나 블록을 사용하여 일련의 선언/정의를 그룹화하여 특정 연결을 가질 수 있습니다.

 extern "C" void foo(int); extern "C" { void g(char); int i; }

기술에 관심이 있다면 C++03 표준의 섹션 7.5에 나열되어 있습니다. 여기에 간단한 요약이 있습니다( extern "C" 강조).

  • extern "C" 는 연결 사양입니다.
  • 모든 컴파일러는 "C" 연결을 제공 해야 합니다.
  • 연결 사양은 네임스페이스 범위에서만 발생합니다.
  • 모든 함수 유형, 함수 이름 및 변수 이름에는 언어 연결이 있습니다. Richard's Comment: 외부 연결이 있는 함수 이름과 변수 이름에만 언어 연결이 있습니다.
  • 별개의 언어 연결을 가진 두 가지 기능 유형은 다른 점에서는 동일하더라도 별개의 유형입니다.
  • 연결 사양 중첩, 내부 사양이 최종 연결을 결정합니다.
  • extern "C" 는 클래스 멤버에 대해 무시됩니다.
  • 특정 이름을 가진 함수는 최대 하나의 "C" 연결을 가질 수 있습니다(네임스페이스에 관계없이).
  • extern "C" 는 함수가 외부 연결을 갖도록 강제합니다(정적으로 만들 수 없음) Richard's comment: static inside extern "C" 는 유효합니다. 이렇게 선언된 엔터티에는 내부 연결이 있으므로 언어 연결이 없습니다.
  • C++에서 다른 언어로 정의된 개체 및 다른 언어에서 C++로 정의된 개체로의 연결은 구현에 따라 정의되고 언어에 따라 다릅니다. 두 언어 구현의 객체 레이아웃 전략이 충분히 유사한 경우에만 이러한 연결을 달성할 수 있습니다.

Faisal Vali

아직 게시되지 않았기 때문에 약간의 정보를 추가하고 싶었습니다.

다음과 같은 C 헤더의 코드를 매우 자주 볼 수 있습니다.

 #ifdef __cplusplus extern "C" { #endif // all of your legacy C code here #ifdef __cplusplus } #endif

이렇게 하면 매크로 "__cplusplus"가 정의되므로 C++ 코드와 함께 해당 C 헤더 파일을 사용할 수 있습니다. 그러나 매크로가 정의되지 않은 레거시 C 코드와 함께 사용할 수도 있으므로 고유한 C++ 구성이 표시되지 않습니다.

하지만 다음과 같은 C++ 코드도 보았습니다.

 extern "C" { #include "legacy_C_header.h" }

내가 상상하는 것은 거의 동일한 것을 달성합니다.

어느 쪽이 더 나은지 확실하지 않지만 둘 다 보았습니다.


UncaAlby

무슨 일이 일어나고 있는지 확인하기 위해 g++ 생성 바이너리를 디컴파일하십시오.

메인.cpp

 void f() {} void g(); extern "C" { void ef() {} void eg(); } /* Prevent g and eg from being optimized away. */ void h() { g(); eg(); }

생성된 ELF 출력을 컴파일하고 디스어셈블합니다.

 g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp readelf -s main.o

출력에는 다음이 포함됩니다.

 8: 0000000000000000 7 FUNC GLOBAL DEFAULT 1 _Z1fv 9: 0000000000000007 7 FUNC GLOBAL DEFAULT 1 ef 10: 000000000000000e 17 FUNC GLOBAL DEFAULT 1 _Z1hv 11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_ 12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv 13: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND eg

해석

우리는 다음을 봅니다.

  • efeg 는 코드와 동일한 이름의 기호로 저장되었습니다.

  • 다른 기호는 엉망이었습니다. 그것들을 풀자:

     $ c++filt _Z1fv f() $ c++filt _Z1hv h() $ c++filt _Z1gv g()

결론: 다음 기호 유형은 모두 맹글링 되지 않았습니다.

  • 한정된
  • 선언되었지만 정의되지 않음( Ndx = UND ), 다른 개체 파일에서 링크 또는 런타임에 제공됨

따라서 다음을 호출할 때 모두 extern "C"

  • C++의 C: g++ gcc 의해 생성된 unmangled 기호를 예상하도록 지시
  • C의 C++: g++ gcc 가 사용할 unmangled 기호를 생성하도록 지시

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 함수에는 맹글링되지 않은 기호가 하나만 있으므로 추가 작업이 필요하지 않습니다.

메인.cpp

 #include <cassert> #include "ch" int main() { assert(f() == 1); }

채널

 #ifndef C_H #define C_H /* This ifdef allows the header to be used from both C and C++ * because C does not know what this extern "C" thing is. */ #ifdef __cplusplus extern "C" { #endif int f(); #ifdef __cplusplus } #endif #endif

참조

 #include "ch" int f(void) { return 1; }

운영:

 g++ -c -o main.o -std=c++98 main.cpp gcc -c -o co -std=c89 cc g++ -o main.out main.o co ./main.out

extern "C" 가 없으면 링크가 다음과 같이 실패합니다.

 main.cpp:6: undefined reference to `f()'

g++ gcc 가 생성하지 않은 f 를 찾을 것으로 예상하기 때문입니다.

GitHub의 예 .

C 예제에서 실행 가능한 최소 C++

C에서 C++를 호출하는 것은 조금 더 어렵습니다. 노출하려는 각 함수의 비 맹글링 버전을 수동으로 만들어야 합니다.

여기에서는 C++ 함수 오버로드를 C에 노출하는 방법을 보여줍니다.

메인.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 가 찾을 수 없는 맹글링된 기호를 생성했기 때문입니다.

GitHub의 예 .

C++에서 C 헤더를 포함할 때 extern "c" 는 어디에 있습니까?

  • cstdio 와 같은 C 헤더의 C++ 버전은 https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html이 언급하는 #pragma GCC system_header 에 의존할 수 있습니다. "RS/6000 AIX, GCC와 같은 일부 대상에서 C++로 컴파일할 때 모든 시스템 헤더를 'extern "C"' 블록으로 암묵적으로 둘러싸고 있습니다." 하지만 완전히 확인하지는 않았습니다.
  • /usr/include/unistd.h 와 같은 POSIX 헤더는 다음에서 다룹니다. 표준 POSIX C 헤더를 포함하려면 extern "C" 블록이 필요합니까? __BEGIN_DECLS 를 통해 Ubuntu 20.04에서 재생산됩니다. __BEGIN_DECLS #include <features.h> 를 통해 포함됩니다.

Ubuntu 18.04에서 테스트되었습니다.


Ciro Santilli 新疆再教育营六四事件法轮功郝海东

모든 C++ 프로그램에서 모든 비정적 함수는 이진 파일에 기호로 표시됩니다. 이러한 기호는 프로그램에서 기능을 고유하게 식별하는 특수 텍스트 문자열입니다.

C에서 기호 이름은 함수 이름과 동일합니다. 이것은 C에서 두 개의 비정적 함수가 같은 이름을 가질 수 없기 때문에 가능합니다.

C++는 오버로딩을 허용하고 클래스, 멤버 함수, 예외 사양과 같이 C에는 없는 많은 기능을 가지고 있기 때문에 단순히 함수 이름을 기호 이름으로 사용할 수 없습니다. 이 문제를 해결하기 위해 C++는 함수 이름과 필요한 모든 정보(예: 인수의 수 및 크기)를 컴파일러와 링커에서만 처리되는 이상한 문자열로 변환하는 소위 이름 맹글링을 사용합니다.

따라서 함수를 extern C로 지정하면 컴파일러에서 이름 맹글링을 수행하지 않으며 해당 기호 이름을 함수 이름으로 사용하여 직접 액세스할 수 있습니다.

이것은 이러한 함수를 호출하기 위해 dlsym()dlopen() 을 사용할 때 편리합니다.


sud03r

C++는 함수 이름을 조작하여 절차적 언어에서 객체 지향 언어를 만듭니다.

대부분의 프로그래밍 언어는 기존 프로그래밍 언어 위에 구축되지 않습니다. C++는 C를 기반으로 구축되었으며, 나아가 절차적 프로그래밍 언어로 구축된 객체 지향 프로그래밍 언어이므로 C와 역호환성을 제공하는 extern "C"

다음 예를 살펴보겠습니다.

 #include <stdio.h> // Two functions are defined with the same name // but have different parameters void printMe(int a) { printf("int: %i\n", a); } void printMe(char a) { printf("char: %c\n", a); } int main() { printMe('a'); printMe(1); return 0; }

AC 컴파일러는 위의 예제를 컴파일하지 않을 것입니다. 왜냐하면 같은 함수인 printMe int a vs char a 다른 매개변수가 있더라도).

gcc -o printMe printMe.c && ./printMe;
오류 1개. PrintMe가 두 번 이상 정의되었습니다.

C++ 컴파일러는 위의 예제를 컴파일합니다. printMe 가 두 번 정의되어도 상관 없습니다.

g++ -o printMe printMe.c && ./printMe;

이는 C++ 컴파일러가 매개변수에 따라 함수의 이름을 암시적으로 변경하기 때문입니다( mangles ). C에서는 이 기능이 지원되지 않았습니다. 그러나 C++가 C를 기반으로 구축될 때 언어는 객체 지향적으로 설계되었으며 동일한 이름의 메서드(함수)로 다른 클래스를 생성하고 다른 기반으로 메서드를 재정의(메소드 재정의)하는 기능을 지원해야 했습니다. 매개변수.

extern "C" 는 "C 함수 이름을 조작하지 마십시오"라고 말합니다.

그러나 다른 레거시 C 파일, "parent.h", "child.h" 등의 s 함수 이름 include 하는 "parent.c"라는 레거시 C 파일이 있다고 상상해 보십시오. 레거시 "parent.c" 파일이 C++ 컴파일러를 통해 실행하면 함수 이름이 수정되고 "parent.h", "child.h" 등에 지정된 함수 이름과 더 이상 일치하지 않습니다. 따라서 해당 외부 파일의 함수 이름도 필요합니다. 망할. 종속성이 많은 복잡한 C 프로그램에서 함수 이름을 변경하면 코드가 깨질 수 있습니다. 따라서 C++ 컴파일러에 함수 이름을 맹글링하지 않도록 지시할 수 있는 키워드를 제공하는 것이 편리할 수 있습니다.

extern "C" 키워드는 C++ 컴파일러에게 C 함수 이름을 맹글링(이름 변경)하지 않도록 지시합니다.

예를 들어:

extern "C" void printMe(int a);


tim-montague

어떤 C 헤더도 단순히 extern "C"로 래핑하여 C++와 호환되도록 만들 수 없습니다. C 헤더의 식별자가 C++ 키워드와 충돌하면 C++ 컴파일러는 이에 대해 불평합니다.

예를 들어, 다음 코드가 g++에서 실패하는 것을 보았습니다.

 extern "C" { struct method { int virtual; }; }

다소 이해가 되지만 C 코드를 C++로 이식할 때 염두에 두어야 할 사항입니다.


Sander Mertens

이 함수는 함수 이름이되지 않음을 의미한다는 것을 C.에서 연습에서 호출 할 것을 같은 방식으로 함수의 링크 변경 망가을 .


Employed Russian

링크 단계에서 C와 C++로 컴파일된 함수의 이름이 다르기 때문에 링크할 때 C 스타일에서 해당 함수의 이름을 조회하도록 C++ 컴파일러에 알립니다.


Mark Rushakoff

extern "C" 는 C++ 컴파일러에서 인식하고 언급된 함수가 C 스타일로 컴파일(또는 컴파일될) 예정임을 컴파일러에 알리기 위한 것이므로 링크하는 동안 C에서 함수의 올바른 버전으로 링크됩니다.


Flami

extern "C" 는 Cpp 소스 파일 에서 C 함수 를 호출하는 데 사용되는 연결 사양입니다. C 함수를 호출하고 변수를 작성하고 헤더를 포함할 수 있습니다 . 함수는 외부 엔티티에서 선언되고 외부에서 정의됩니다. 구문은

유형 1:

 extern "language" function-prototype

유형 2:

 extern "language" { function-prototype };

예:

 #include<iostream> using namespace std; extern "C" { #include<stdio.h> // Include C Header int n; // Declare a Variable void func(int,int); // Declare a function (function prototype) } int main() { func(int a, int b); // Calling function . . . return 0; } // Function definition . . . void func(int m, int n) { // // }

Yogeesh H T

나는 dll(동적 링크 라이브러리) 파일에 대해 'extern "C"'를 사용하여 main() 함수를 "내보내기 가능"하게 만들어서 나중에 dll의 다른 실행 파일에서 사용할 수 있도록 했습니다. 내가 사용한 곳의 예가 유용할 수 있습니다.

DLL

 #include <string.h> #include <windows.h> using namespace std; #define DLL extern "C" __declspec(dllexport) //I defined DLL for dllexport function DLL main () { MessageBox(NULL,"Hi from DLL","DLL",MB_OK); }

EXE

 #include <string.h> #include <windows.h> using namespace std; typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll Function mainDLLFunc;//make a variable for function placeholder int main() { char winDir[MAX_PATH];//will hold path of above dll GetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe strcat(winDir,"\\exmple.dll");//concentrate dll name with path HINSTANCE DLL = LoadLibrary(winDir);//load example dll if(DLL==NULL) { FreeLibrary((HMODULE)DLL);//if load fails exit return 0; } mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL, "main"); //defined variable is used to assign a function from dll //GetProcAddress is used to locate function with pre defined extern name "DLL" //and matcing function name if(mainDLLFunc==NULL) { FreeLibrary((HMODULE)DLL);//if it fails exit return 0; } mainDLLFunc();//run exported function FreeLibrary((HMODULE)DLL); }

SturmCoder

이 답변은 참을성 없는/마감 시간이 있는 사람을 위한 것입니다. 부분/간단한 설명만 아래에 있습니다.

  • C++에서는 오버로딩을 통해 클래스에 동일한 이름을 가질 수 있습니다(예: 모두 동일한 이름이므로 dll에서 있는 그대로 내보낼 수 없습니다). 이러한 문제에 대한 솔루션은 다른 문자열(심볼이라고 함)로 변환되는 것입니다 ), 기호는 함수의 이름과 인수를 설명하므로 동일한 이름을 가진 각 함수를 고유하게 식별할 수 있습니다(이름 맹글링이라고도 함).
  • C에서는 오버로딩이 없고 함수 이름이 고유합니다(따라서 함수 이름을 고유하게 식별하기 위한 별도의 문자열이 필요하지 않으므로 기호는 함수 이름 자체임)

그래서
C++에서 이름 맹글링으로 각 기능을 고유하게 식별
C에서는 이름 맹글링이 없어도 각 기능을 고유하게 식별합니다.

C++의 동작을 변경하려면, 즉 특정 함수에 대해 이름 맹글링이 발생하지 않도록 지정하려면 dll에서 특정 이름의 함수를 내보내는 것과 같이 어떤 이유로든 함수 이름 앞에 extern "C"를 사용할 수 있습니다. , 클라이언트가 사용할 수 있습니다.

더 자세한/더 정확한 답변을 보려면 다른 답변을 읽으십시오.


Manohar Reddy Poreddy

C 컴파일러에 의해 컴파일된 함수 void f()와 C++ 컴파일러에 의해 컴파일된 같은 이름의 함수 void f()는 같은 함수가 아닙니다. 해당 함수를 C로 작성한 다음 C++에서 호출하려고 하면 링커는 C++ 함수를 찾고 C 함수를 찾지 않습니다.

extern "C"는 C++ 컴파일러에 C 컴파일러에 의해 컴파일된 함수가 있음을 알려줍니다. C 컴파일러에 의해 컴파일되었다고 말하면 C++ 컴파일러는 이를 올바르게 호출하는 방법을 알게 됩니다.

또한 C++ 컴파일러는 C 컴파일러가 호출할 수 있는 방식으로 C++ 함수를 컴파일할 수 있습니다. 그 함수는 공식적으로는 C 함수이지만 C++ 컴파일러에 의해 컴파일되기 때문에 모든 C++ 기능을 사용할 수 있고 모든 C++ 키워드를 갖습니다.


gnasher729

C와 C++를 혼합할 때(즉, a. C++에서 C 함수 호출, b. C에서 C++ 함수 호출), C++ 이름 맹글링으로 인해 연결 문제가 발생합니다. 기술적으로 말하면 이 문제는 호출 수신자 함수가 해당 컴파일러를 사용하여 이미 바이너리(대부분 *.a 라이브러리 파일)로 컴파일된 경우에만 발생합니다.

따라서 C++에서 이름 맹글링을 비활성화하려면 extern "C"를 사용해야 합니다.


Trombe

다른 좋은 답변과 충돌하지 않고 약간의 예를 추가하겠습니다.

정확히 C++ 컴파일러 가 하는 일: 컴파일 과정에서 이름을 엉망으로 만들기 때문에 컴파일러에게 C 구현을 특별히 취급하도록 지시해야 합니다.

C++ 클래스를 만들고 extern "C" 추가할 때 C++ 컴파일러에 C 호출 규칙을 사용하고 있음을 알리는 것입니다.

이유(C++에서 C 구현을 호출함): C++에서 C 함수를 호출하거나 C에서 C++ 함수를 호출하려고 합니다(C++ 클래스 ... 등은 C에서 작동하지 않음).


Susobhan Das

extern "C" 사용법에 대한 geeks 설명은 geeks for geeks인 아래 링크를 참조하십시오. 아래 페이지에서 중요한 정보 추가

f() 함수의 다음 선언을 고려하십시오.

 int f (void) { return 1; } int f (int) { return 0; } void g (void) { int i = f(), j = f(0); }

C++ 컴파일러는 위의 이름을 다음으로 맹글링할 수 있습니다(출처: Wiki)

 int __f_v (void) { return 1; } int __f_i (int) { return 0; } void __g_v (void) { int i = __f_v(), j = __f_i(0); }

https://www.geeksforgeeks.org/extern-c-in-c/


Aryaman Gupta

출처 : http:www.stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c

반응형