C ++와 C 결합-#ifdef __cplusplus는 어떻게 작동합니까?


319

레거시 C 코드 가 많은 프로젝트를 진행 중입니다 . 우리는 결국 레거시 코드를 변환하려는 의도로 C ++로 작성하기 시작했습니다. C 와 C ++의 상호 작용 방식에 대해 약간 혼란스러워합니다 . 나는 포장하여 해당 이해 C를 사용하여 코드를 extern "C"망글하지 않습니다 컴파일러 ++은 C 의 C 코드의 이름을,하지만 난 완전히 확인이 구현하는 방법을 모르겠어요.

따라서 각 C 헤더 파일 의 상단 (포함 가드 후)에는

#ifdef __cplusplus
extern "C" {
#endif

그리고 맨 아래에

#ifdef __cplusplus
}
#endif

이 둘 사이에는 include, typedef 및 함수 프로토 타입이 모두 있습니다. 이것을 올바르게 이해하고 있는지 확인하기 위해 몇 가지 질문이 있습니다.

  1. C 헤더 파일 Bh를 포함하고 다른 C 헤더 파일 Ch 를 포함하는 C ++ 파일 A.hh 가 있으면 어떻게 작동합니까? 컴파일러가 Bh로 들어가면 __cplusplus정의되어 코드를 래핑합니다 extern "C" ( __cplusplus이 블록 안에 정의되지 않음). 따라서 Ch로 들어가면 __cplusplus정의되지 않으며 코드가 래핑되지 않습니다 extern "C". 이 올바른지?

  2. 코드 조각을 래핑하는 데 문제가 extern "C" { extern "C" { .. } }있습니까? 두 번째 extern "C" 는 무엇을 할 것인가?

  3. 이 래퍼는 .c 파일 주위에 배치하지 않고 .h 파일에만 배치합니다. 함수에 프로토 타입이 없으면 어떻게됩니까? 컴파일러는 이것이 C ++ 함수라고 생각합니까?

  4. 또한 C 로 작성된 타사 코드를 사용하고 있으며 이러한 종류의 래퍼가 없습니다. 해당 라이브러리의 헤더를 포함 할 때마다 extern "C"#include를 둘러 쌉니다. 이것이 올바른 처리 방법입니까?

  5. 마지막으로, 이것이 좋은 아이디어입니까? 우리가해야 할 다른 일이 있습니까? 우리는 가까운 미래에 C 와 C ++를 혼합 할 예정이며, 모든 기반을 다룰 것입니다.



2
간결하게, 이것은 가장 좋은 설명입니다 : To ensure that the names declared in that portion of code have C linkage, and thus C++ name mangling is not performed. ( 링크 에서 찾았습니다 )
anhldbk

당신은 대담에서 C 언어의 이름을 넣어 필요가 없습니다
에드워드 가락

답변:


290

extern "C"컴파일러가 코드를 읽는 방식을 실제로 바꾸지는 않습니다. 코드가 .c 파일에 있으면 C로 컴파일되고 .cpp 파일에 있으면 C ++로 컴파일됩니다 (구성에 이상한 일이없는 한).

extern "C"연결에 영향을주는 것은 무엇입니까? C ++ 함수는 컴파일 될 때 이름이 엉망이되어 과부하가 가능합니다. 함수 이름은 매개 변수의 유형과 개수에 따라 수정되므로 같은 이름을 가진 두 함수의 심볼 이름이 달라집니다.

내부의 코드 extern "C"는 여전히 C ++ 코드입니다. extern "C"블록에서 수행 할 수있는 작업에는 제한이 있지만 모두 연결에 관한 것입니다. C 연결로 만들 수없는 새 심볼을 정의 할 수 없습니다. 예를 들어 클래스 나 템플릿이 없음을 의미합니다.

extern "C"블록이 멋지게 중첩됩니다. 이이기도 extern "C++"자신이 희망의 안에 갇혀 발견하면 extern "C"지역, 그러나 그것은 청결 관점에서 그런 좋은 생각이 아니다.

이제 구체적으로 번호가 매겨진 질문과 관련하여 :

# 1과 관련하여 __cplusplus는 extern "C"블록 내부에서 정의 된 상태를 유지 합니다. 블록이 깔끔하게 중첩되어야하기 때문에 이것은 중요하지 않습니다.

# 2와 관련하여 __cplusplus는 C ++ 컴파일러를 통해 실행되는 모든 컴파일 단위에 대해 정의됩니다. 일반적으로 이는 .cpp 파일과 해당 .cpp 파일에 포함 된 모든 파일을 의미합니다. 다른 컴파일 단위에 포함 된 경우 동일한 .h (또는 .hh 또는 .hpp 또는 what-have-you)를 다른 시간에 C 또는 C ++로 해석 할 수 있습니다. .h 파일의 프로토 타입이 C 기호 이름을 참조하도록하려면 extern "C"C ++로 해석 될 때 있어야 하고 C extern "C"로 해석 될 때 없어야합니다 #ifdef __cplusplus.

질문 # 3에 대답하기 위해 : 프로토 타입이없는 함수는 extern "C"블록 내부가 아닌 .cpp 파일에 있으면 C ++ 연결을 갖습니다 . 프로토 타입이없는 경우 동일한 파일의 다른 함수에서만 호출 할 수 있으며 일반적으로 해당 함수를 가질 계획이 없기 때문에 링키지의 모양을 신경 쓰지 않기 때문에 괜찮습니다. 어쨌든 같은 컴파일 유닛 외부의 어떤 것에 의해 호출됩니다.

# 4의 경우 정확히 알 수 있습니다. C 링크가있는 코드 (예 : C 컴파일러에서 컴파일 한 코드)에 extern "C"대한 헤더를 포함하는 경우 라이브러리와 링크 할 수있는 헤더가 있어야합니다. (그렇지 않으면, 링커는 찾고 _Z1hic있을 때 와 같은 이름을 가진 함수를 찾고있을 것입니다void h(int, char)

5 : 이런 종류의 믹싱은 사용하는 일반적인 이유 extern "C"이며, 이런 식으로 잘못하는 것이 보이지 않습니다. 자신이하는 일을 이해해야합니다.


10
extern "C++"C ++ 헤더 / 코드가 일부 C 코드 내부에 갇혀있을 때 언급 하기에 좋습니다
deddebme

1
간단한 C 프로그램을 작성했습니다. 그 안에 #ifdef __cplusplus 블록을 추가하고 printf ( "__ cplusplus defined \ n"); 그 안에. gcc로 컴파일하면 "__cplusplus defined"가 인쇄되지 않지만 g ++로 컴파일하면 인쇄됩니다. 그래서 __cplusplus는 컴파일러가 C ++ 컴파일러라는 것을 의미합니다. 정확하지 않습니까? ( '__cplusplus는 extern "C"블록 안에 정의되어야합니다. "라고 말했기 때문에 __cplusplus를 명시 적으로 정의 할 수 있습니까?
Chan Kim

1
원하는 것을 (거의) 정의 할 수 있어야하지만, 요점은 vs를 사용 __cplusplus하고 있는지 여부를 결정 하는 것입니다. 따라서 수동 / 명시 적으로 정의하면 목적이 무시됩니다.C++C
nurchi

extern "C"는 실제로 컴파일러가 소스 파일을 보는 방법이 아니라 헤더 파일을 보는 방법에 관한 것입니다. 구조체는 C와 C ++로 컴파일 할 때 크기가 다를 수 있으며 물론 이름 맹 글링이 있으며 잠재적으로 다른 차이점도 있습니다.
Nick

39
  1. extern "C"__cplusplus매크로의 유무를 변경하지 않습니다 . 래핑 된 선언의 연결 및 이름 관리 만 변경합니다.

  2. extern "C"블록을 매우 행복하게 중첩 할 수 있습니다 .

  3. .c파일을 C ++로 컴파일 하면 extern "C"블록에없고 extern "C"프로토 타입이 없는 것은 C ++ 함수로 처리됩니다. C로 컴파일하면 모든 것이 C 함수가됩니다.

  4. 이런 식으로 C와 C ++를 안전하게 혼합 할 수 있습니다.


컴파일하면 .cC로 파일을 ++, 다음 모든 것이가에있는 경우에도 C ++ 코드로 컴파일 extern "C"블록. 이 extern "C"코드는 C ++ 호출 규칙 (예 : 연산자 오버로드)에 의존하는 기능을 사용할 수 없지만 함수 본문은 여전히 ​​C ++로 컴파일됩니다.
David C.

21

Andrew Shelansky의 훌륭한 답변과 함께 약간의 의견에 동의 하지 않는 컴파일러가 코드를 읽는 방식을 실제로 바꾸지는 않습니다.

함수 프로토 타입은 C로 컴파일되기 때문에 다른 매개 변수를 사용하여 동일한 함수 이름을 오버로드 할 수 없습니다. 이는 컴파일러 이름 변경의 주요 기능 중 하나입니다. 연결 문제로 설명되어 있지만 사실은 아닙니다. 컴파일러와 링커 모두에서 오류가 발생합니다.

오버로드와 같은 프로토 타입 선언의 C ++ 기능을 사용하려고하면 컴파일러 오류가 발생합니다.

함수를 찾을 수 없습니다 나타납니다 때문에이 경우 링커 오류가 나중에 발생 하지통근 "C" 선언 주위에 래퍼를 헤더가 C 및 C ++ 소스의 혼합물에 포함되어 있습니다.

사람들이 C 컴파일을 C ++ 설정 으로 사용하지 못하게하는 한 가지 이유 는 소스 코드를 더 이상 이식 할 수 없기 때문입니다. 이 설정은 프로젝트 설정이므로 .c 파일을 다른 프로젝트에 놓으면 c ++로 컴파일되지 않습니다. 오히려 사람들은 파일 접미사 이름을 .cpp로 바꾸는 데 시간이 걸립니다.


1
이것은 내 머리를 잡아 당기는 비밀스러운 원인이었습니다. 어딘가에 게시해야합니다.
Mitchell Currie

3

C와 C ++ 애플리케이션이 아무런 문제없이 C 인터페이스를 사용하도록하기 위해 ABI에 관한 것입니다.

C 언어는 매우 쉬우므로 코드 생성은 GCC, Borland C \ C ++, MSVC 등과 같은 다른 컴파일러에서 수년 동안 안정적이었습니다.

C ++이 점점 더 대중화되면서 새로운 C ++ 도메인에 많은 것들이 추가되어야합니다 (예를 들어 Cfront는 C & C가 필요한 모든 기능을 처리 할 수 ​​없기 때문에 AT & T에서 포기했습니다). 같은 템플릿 과거의 기능과 컴파일 타임 코드 생성, 다른 컴파일러가 실제로 별도로 C ++ 컴파일러 및 링커의 실제 구현 한 벤더 실제 ABI를 다른 플랫폼에서 C ++ 프로그램과 전혀 호환되지 않습니다.

사람들은 여전히 ​​C ++로 실제 프로그램을 구현하고 싶지만 평소와 같이 여전히 오래된 C 인터페이스와 ABI를 유지하고 헤더 파일은 extern "C"{} 을 선언 해야하며 컴파일러 는 호환 가능 / 오래된 / 단순 / 쉬운 C ABI를 생성합니다 컴파일러가 C ++ 컴파일러가 아닌 C 컴파일러 인 경우 인터페이스 기능에 사용 됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.