【问题标题】:When to use extern "C" in simple words? [duplicate]什么时候用简单的单词使用 extern "C"? [复制]
【发布时间】:2011-02-17 07:34:27
【问题描述】:

也许我不了解 C 和 C++ 之间的区别,但我们何时以及为什么需要使用

extern "C" {

?显然它是一个“链接约定”。

我简要地阅读了它,并注意到 MSVS 中包含的所有 .h 头文件都用它包围了它们的代码。究竟什么类型的代码是“C 代码”而不是“C++ 代码”?我以为 C++ 包含所有 C 代码?

我猜测情况并非如此,C++ 是不同的,标准特性/功能存在于其中一个或另一个但不是两者都存在(即:printf 是 C 而 cout 是 C++),但 C++ 是倒退的通过外部“C”声明兼容。这是正确的吗?

我的下一个问题取决于第一个问题的答案,但无论如何我都会在这里问:由于用 C 编写的 MSVS 头文件被 extern "C" { ... } 包围,你什么时候需要自己在自己的代码中使用它?如果您的代码是 C 代码并且您尝试在 C++ 编译器中对其进行编译,那么它是否应该毫无问题地工作,因为您包含的所有标准 h 文件都已经在 C++ 编译器中包含了 extern "C" 的东西?

在 C++ 中编译但链接到已构建的 C 库或其他东西时是否必须使用它?

【问题讨论】:

标签: c++ c extern-c


【解决方案1】:

当声明在 C 中实现/编译的函数时,您需要在 C++ 中使用 extern "C"extern "C" 的使用告诉编译器/链接器使用 C 命名和调用约定,而不是 C++ 名称修改和 C++ 调用约定,否则将使用。对于其他库提供的函数,您几乎不需要使用extern "C",因为编写良好的库已经为它导出到 C 和 C++ 的公共 API 提供了它。但是,如果您编写了一个希望在 C 和 C++ 中都可用的库,那么您必须有条件地将它放在头文件中。

至于是否所有的C代码都是C++代码……不,那是不正确的。 C++ 是“C 的超集”是一个流行的神话。虽然 C++ 确实努力尽可能与 C 兼容,但还是有一些 incompatibilities。例如,bool 是有效的 C++ 但不是有效的 C,而 _Bool 在 C99 中存在,但在 C++ 中不可用。

至于您是否需要将 extern "C" 与系统的 ".h" 文件一起使用....任何精心设计的实现都会为您提供这些,因此您不需要使用它们.但是,为了确保提供了它们,您应该包含以“c”开头并省略“.h”的等效头文件。例如,如果您包含 ,几乎所有合理的系统都会添加 extern "C";但是,为了确保 C++ 兼容的标头,您应该包含标头 .

您可能还对Mixing C and C++ from the C++ FAQ Lite感兴趣。

【讨论】:

  • 感谢大家的回复。那么我是否只需要在包含已经在 C 编译器中编译的代码时才使用它?
  • @Russel,是的,假设您从 C++ 中包含它,并假设标头尚未使用 extern "C"。
  • 啊,这就是为什么在 C++ 中调用 C 函数会出现链接和符号错误...
【解决方案2】:

其他答案是正确的,但一个完整的“样板”示例可能会有所帮助。在 C 和/或 C++ 项目中包含 C 代码的规范方法如下:

//
// C_library.h
//

#ifdef __cplusplus
extern "C" {
#endif

//
// ... prototypes for C_library go here ...
//

#ifdef __cplusplus
}
#endif

-

//
// C_library.c
//

#include "C_library.h"

//
// ... implementations for C_library go here ...
//

-

//
// C++_code.cpp
//

#include "C_library.h"
#include "C++_code.h"

//
// ... C++_code implementation here may call C functions in C_library.c ...
//

注意:以上内容也适用于从 Objective-C++ 调用 C 代码。

【讨论】:

  • 很好的例子,谢谢。我读过 C++ 的调用约定。(_cdecl、_stdcall、_fastcall 等)。 C 是否只有一种调用约定??
  • @Russel:_cdecl、_stdcall、_fastcall 等都是非标准的 Windows 特定的可憎之物,在文明世界中是找不到的。 ;-)
  • 我很抱歉 :) 谁能给我指出比 MSDN 更好的调用约定文档?另外,C 是否只有一种标准调用约定?
  • 是的,标准 C 确实只有一种调用约定,但是除了前面提到的微软可恶之外,您还可能遇到各种非标准扩展,例如 farpascal,举两个例子。
【解决方案3】:

C++ 编译器对符号表中名称的处理方式与 C 编译器不同。您需要使用 extern "C" 声明来告诉 C++ 编译器在构建符号表时使用 C 修改约定。

【讨论】:

    【解决方案4】:

    我使用“extern c”,以便 C# 可以读取我的 C++ 代码,而无需弄清楚导出 C++ dll 函数时所做的额外名称修改。否则,为了正确访问 dll 中的 C++ 函数,我必须在 C# 端的函数入口点末尾添加额外的无意义(或实际上是非英语)字符。

    【讨论】:

      【解决方案5】:

      extern "C" {} 块告诉 C++ 编译器使用 C 命名和调用约定。如果您不使用它,如果尝试在 C++ 项目中包含 C 库,则会出现链接器错误,因为 C++ 会破坏名称。我倾向于在我所有的 C 头文件上使用它,以防它们曾经在 C++ 项目中使用:

      #ifdef __cplusplus
      extern "C" {
      #endif
      
      /* My library header */
      
      #ifdef __cplusplus
      } // extern
      #endif
      

      【讨论】:

        【解决方案6】:

        当您想在由 C++ 编译器编译的代码中使用 C 调用约定时,您需要使用 extern "C"。这有两个原因:

        • 您有一个用 C 实现的函数,并希望从 C++ 调用它。

        • 您有一个用 C++ 实现的函数,并希望从 C 中调用它。请注意,在这种情况下,您只能在函数接口中使用 C++ 的 C 部分(不能使用类,...)。

        除了 C 之外,当您希望 C++ 和其他使用与 C 相同的调用和命名约定的语言之间进行互操作时,这也适用。

        通常,C 头文件中的声明用

        包围
        #ifdef __cplusplus
        extern "C" {
        #endif
        
        [... C declarations ...]
        
        #ifdef __cplusplus
        }
        #endif
        

        使其可以在 C++ 中使用。

        【讨论】:

          【解决方案7】:

          C++ 函数受name mangling 约束。这使得它们无法直接从 C 代码调用,除非使用 extern "C"

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-09-15
            • 1970-01-01
            • 2015-03-18
            • 2012-04-13
            • 2010-09-11
            • 1970-01-01
            相关资源
            最近更新 更多