简单来说,什么时候需要使用extern "C"?

38

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

extern "C" {

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

我简略地了解了一下,注意到MSVS附带的所有.h头文件都用它包围了它们的代码。什么样的代码属于“C代码”,而不是“C++代码”?我认为C++包括所有C代码,对吗?

我猜这并不是这样,C++与C不同,并且标准特性/函数存在于其中一个而不是两者(例如:printf是C,cout是C++),但C++通过extern "C"声明向后兼容。这样正确吗?

我的下一个问题取决于第一个问题的答案,但我还是在这里问了:既然用C编写的MSVS头文件被extern "C"{...}所包围,那么你何时需要在自己的代码中使用它呢?如果您的代码是C代码,并尝试在C++编译器中进行编译,由于您包含的所有标准头文件都已经有了extern "C",因此不会有任何问题,不需要添加这个声明,对吗?

那么什么情况下需要在C++中编译,但连接到已构建的C库或其他东西时使用这个声明呢?


7
C++不是C语言的超集。有些合法的C语言代码在C++中是不合法的。 - Yann Ramin
theatrus是正确的:https://dev59.com/v3E85IYBdhLWcg3wgDvd - nc3b
1
@YannRamin 你应该链接一些有用的东西来验证你的论点。 - Michael Pacheco
7个回答

41
在C++中,当声明一个在C中实现/编译的函数时,需要使用extern "C"。使用extern "C"告诉编译器/链接器使用C命名和调用约定,而不是使用C++名称重整和C++调用约定。对于其他库提供的函数,你几乎永远不需要使用extern "C",因为良好编写的库将已经包含了这个选项以导出适用于C和C++的公共API。但是,如果你想编写一个既可以在C中又可以在C++中使用的库,那么就必须有条件地将它放在头文件中。
至于所有C代码是否都是C++代码这一问题...... 不,这是一个流传的谬论。虽然C++确保尽可能与C兼容,但存在一些不兼容性。例如,bool在C++中有效但不在C中有效,而_Bool存在于C99中,但在C++中不可用。
至于系统“。h”文件是否需要使用extern "C"....任何设计良好的实现都会为你提供这些内容,因此你无需使用它们。但是,为了确保它们被提供,你应该包含以“c”开头并省略“.h”的等效头文件。例如,如果你包含<ctype.h>,几乎任何合理的系统都会添加extern "C";但是,为了确保与C++兼容的头文件,你应该包含头文件<cctype>。
你可能还对来自C++ FAQ Lite的混合使用C和C++感兴趣。

谢谢大家的回复。那么,我只需要在包含已经在C编译器中编译过的代码时使用它吗? - Russel
1
@Russel,是的,假设您是从C++中包含它,并且假设头文件尚未使用extern "C"。 - Michael Aaron Safyan
啊,原来这就是在C++中调用C函数会导致链接和符号错误的原因啊... - Abdel Aleem

27

其他答案是正确的,但一个完整的“样板”示例可能会有所帮助。在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代码。

1
非常好的例子,谢谢。我已经阅读了有关C++调用约定的内容(_cdecl、_stdcall、_fastcall等)。C语言只有一种调用约定吗? - Russel
1
@Russel:_cdecl,_stdcall,_fastcall等都是非标准的Windows专有形式,这些在文明世界中是找不到的。;-) - Paul R
1
抱歉 :) 有人可以指向比MSDN更好的关于调用约定的文档吗?此外,C语言只有一种标准的调用约定吗? - Russel
1
是的,标准的C确实只有一种调用约定,但除了前面提到的Microsoft的可怕之外,还有各种非标准扩展,比如farpascal等。 - Paul R

17

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


3

我使用'extern c',这样C#就可以读取我的C++代码,而不必去解决导出C++ dll函数时所做的额外名称重整问题。否则,在C#端,我必须在函数入口处添加额外的无意义(或者说非英文)字符,以便正确访问dll中的C++函数。


2

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

#ifdef __cplusplus
extern "C" {
#endif

/* My library header */

#ifdef __cplusplus
} // extern
#endif

2
当您想在使用C++编译器编译的代码中使用C调用约定时,需要使用extern "C"。这有两个原因:
  • 您有一个在C中实现的函数,并希望从C++中调用它。

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

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

通常,C头文件中的声明会被包含在extern "C"中。

#ifdef __cplusplus
extern "C" {
#endif

[... C declarations ...]

#ifdef __cplusplus
}
#endif

使其能够从C++中使用。


1

C++函数受到名称重整的影响。这使得它们无法直接从C代码中调用,除非使用extern "C"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接