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

extern "C"C ++ 코드에 정확히 들어가는 것은 무엇입니까 ?

예를 들면 다음과 같습니다.

extern "C" {
   void foo();
}



답변

extern “C”는 C ++에서 function-name에 ‘C’연결 (컴파일러가 이름을 엉망으로 만들지 않음)을 만들도록하여 클라이언트 C 코드가 함수 선언. 함수 정의는 클라이언트 ‘C’링커가 ‘C’이름을 사용하여 연결할 바이너리 형식 (C ++ 컴파일러에서 컴파일 한)으로 포함됩니다.

C ++에는 함수 이름이 오버로드되고 C는 그렇지 않으므로 C ++ 컴파일러는 함수 이름을 링크 할 고유 ID로 사용할 수 없으므로 인수에 대한 정보를 추가하여 이름을 엉망으로 만듭니다. C에서 함수 이름을 오버로드 할 수 없으므로 AC 컴파일러는 이름을 맹 글링 할 필요가 없습니다. C ++에서 함수에 extern “C”연결이 있다고 C ++ 컴파일러는 사용 된 이름에 인수 / 매개 변수 유형 정보를 추가하지 않습니다. 결합.

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

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

기술에 관심이 있다면 C ++ 03 표준의 7.5 절에 나와 있습니다. 다음은 간략한 요약입니다 (extern “C”에 중점을 둡니다).

  • extern “C”는 연결 사양입니다.
  • 모든 컴파일러는 “C”연결을 제공 해야 합니다.
  • 연결 사양은 네임 스페이스 범위에서만 발생해야합니다.
  • 모든 함수 유형, 함수 이름 및 변수 이름에 언어 연결이 있음 Richard의 설명 참조 : 외부 링크가있는 함수 이름 및 변수 이름에만 언어 연결이 있습니다.
  • 서로 다른 언어 연결을 가진 두 가지 함수 유형은 다른 경우에도 구별 유형입니다.
  • 연계 사양 중첩, 내부 연계는 최종 연계를 결정합니다.
  • 클래스 멤버의 extern “C”는 무시됩니다.
  • 특정 이름을 가진 최대 하나의 함수는 네임 스페이스에 관계없이 “C”링크를 가질 수 있습니다.
  • extern “C”는 함수가 외부 연결을 갖도록합니다 (정적화할 수 없음) Richard의 설명을 참조하십시오. ‘extern “C”‘내의 ‘static’은 유효합니다. 이렇게 선언 된 엔터티에는 내부 연결이 있으므로 언어 ​​연결이 없습니다.
  • C ++에서 다른 언어로 정의 된 객체 및 다른 언어에서 C ++로 정의 된 객체로의 연결은 구현 정의 및 언어에 따라 다릅니다. 두 언어 구현의 객체 레이아웃 전략이 유사한 경우에만 그러한 연결을 달성 할 수 있습니다

답변

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

다음과 같이 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"
}

나는 똑같은 일을 성취한다고 상상합니다.

어느 쪽이 더 좋은지 잘 모르겠지만 둘 다 보았습니다.


답변

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

main.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++에 의해 생산 unmangled 문자 기대gcc
  • C의 C ++ : 사용할 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++
 * because C does not know what this extern "C" thing is. */
#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생산하지 않았다.

GitHub의 예 .

C 예제에서 실행 가능한 최소 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입니다.

GitHub의 예 .

우분투에서 테스트 18.04.


답변

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

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

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

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

이러한 기능 을 사용 dlsym()하고 dlopen()호출 할 때 편리 합니다.


답변

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

대부분의 프로그래밍 언어는 기존 프로그래밍 언어를 기반으로하지 않습니다. C ++는 C를 기반으로 구축되었으며, 절차 적 프로그래밍 언어로 구축 된 객체 지향 프로그래밍 언어이므로 C extern "C"와의 호환성을 제공하는 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와 매개 변수가 다르더라도 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 함수 이름을 맹 글링하지 마십시오”

그러나 include다른 레거시 C 파일 “parent.h”, “child.h”등의 함수 이름 인 “parent.c”라는 레거시 C 파일이 있다고 가정합니다. 레거시 “parent.c”파일이 실행되는 경우 C ++ 컴파일러를 통해 함수 이름이 엉망이되어 더 이상 “parent.h”, “child.h”등에 지정된 함수 이름과 일치하지 않으므로 외부 파일의 함수 이름도 엉망이다 의존성이 많은 복잡한 C 프로그램에서 함수 이름을 다루면 코드가 깨질 수 있습니다. 따라서 C ++ 컴파일러에게 함수 이름을 맹 글링하지 않도록 지시 할 수있는 키워드를 제공하는 것이 편리 할 수 ​​있습니다.

extern "C"키워드는에 압착 롤러 (이름 바꾸기) C 함수 이름은 C ++ 컴파일러를 알려줍니다.

예를 들면 다음과 같습니다.

extern "C" void printMe(int a);


답변

extern “C”로 감싸서 C 헤더를 C ++과 호환되도록 만들 수는 없습니다. C- 헤더의 식별자가 C ++ 키워드와 충돌하면 C ++ 컴파일러는 이에 대해 불평합니다.

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

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

Kinda는 의미가 있지만 C 코드를 C ++로 이식 할 때 명심해야합니다.


답변

C에서 함수를 호출 할 수있는 방식으로 함수의 링크를 변경합니다. 실제로 함수 이름이 엉망 이되지 않습니다 .


카테고리C#

답글 남기기