结合C ++和C - #ifdef __cplusplus如何工作?

本文翻译自:Combining C++ and C - how does #ifdef __cplusplus work?

I'm working on a project that has a lot of legacy C code. 我正在开发一个包含大量遗留C代码的项目。 We've started writing in C++, with the intent to eventually convert the legacy code, as well. 我们已经开始用C ++编写,目的是最终转换遗留代码。 I'm a little confused about how the C and C++ interact. 我对C和C ++的交互方式有点困惑。 I understand that by wrapping the C code with extern "C" the C++ compiler will not mangle the C code's names, but I'm not entirely sure how to implement this. 我理解通过用extern "C"包装C代码,C ++编译器不会破坏C代码的名称,但我不完全确定如何实现它。

So, at the top of each C header file (after the include guards), we have 因此,在每个C头文件的顶部(在包含警卫之后),我们有

#ifdef __cplusplus
extern "C" {

and at the bottom, we write 在底部,我们写

#ifdef __cplusplus

In between the two, we have all of our includes, typedefs, and function prototypes. 在两者之间,我们拥有所有的includes,typedef和函数原型。 I have a few questions, to see if I'm understanding this correctly: 我有几个问题,看看我是否理解正确:

  1. If I have a C++ file A.hh which includes a C header file Bh, includes another C header file Ch, how does this work? 如果我有一个C ++文件A.hh,其中包含一个C头文件Bh,包含另一个C头文件Ch,这是如何工作的? I think that when the compiler steps into Bh, __cplusplus will be defined, so it will wrap the code with extern "C" (and __cplusplus will not be defined inside this block). 我认为当编译器进入Bh时,将定义__cplusplus ,因此它将使用extern "C"包装代码(并且不会在此块中定义__cplusplus )。 So, when it steps into Ch, __cplusplus will not be defined and the code will not be wrapped in extern "C" . 因此,当它进入Ch时,将不会定义__cplusplus ,并且代码将不会包含在extern "C" Is this correct? 它是否正确?

  2. Is there anything wrong with wrapping a piece of code with extern "C" { extern "C" { .. } } ? extern "C" { extern "C" { .. } }包装一段代码有什么问题吗? What will the second extern "C" do? 第二个extern "C"做什么?

  3. We don't put this wrapper around the .c files, just the .h files. 我们不把这个包装器放在.c文件周围,只放在.h文件中。 So, what happens if a function doesn't have a prototype? 那么,如果函数没有原型会发生什么? Does the compiler think that it's a C++ function? 编译器是否认为它是C ++函数?

  4. We are also using some third-party code which is written in C , and does not have this sort of wrapper around it. 我们还使用了一些用C语言编写的第三方代码,并没有这种包装。 Any time I include a header from that library, I've been putting an extern "C" around the #include. 每当我从该库中包含一个标题时,我就会在#include周围放置一个extern "C" Is this the right way to deal with that? 这是处理这个问题的正确方法吗?

  5. Finally, is this set up a good idea? 最后,这是一个好主意吗? Is there anything else we should do? 还有什么我们应该做的吗? We're going to be mixing C and C++ for the foreseeable future, and I want to make sure we're covering all our bases. 我们将在可预见的未来混合C和C ++,我想确保我们覆盖所有基础。




A couple of gotchas that are colloraries to Andrew Shelansky's excellent answer and to disagree a little with doesn't really change the way that the compiler reads the code 安德鲁·谢兰斯基(Andrew Shelansky)的优秀答案以及不同意的一些问题并没有真正改变编译器读取代码的方式。

Because your function prototypes are compiled as C, you can't have overloading of the same function names with different parameters - that's one of the key features of the name mangling of the compiler. 因为您的函数原型被编译为C,所以不能使用不同的参数重载相同的函数名称 - 这是编译器名称修改的关键特性之一。 It is described as a linkage issue but that is not quite true - you will get errors from both the compiler and the linker. 它被描述为链接问题,但事实并非如此 - 您将从编译器和链接器中获得错误。

The compiler errors will be if you try to use C++ features of prototype declaration such as overloading. 如果您尝试使用原型声明的C ++功能(例如重载),则会出现编译器错误。

The linker errors will occur later because your function will appear to not be found, if you do not have the extern "C" wrapper around declarations and the header is included in a mixture of C and C++ source. 链接器错误将在稍后发生,因为如果您没有 外部“C”包装声明并且标头包含在C和C ++源的混合中,则您的函数似乎无法找到。

One reason to discourage people from using the compile C as C++ setting is because this means their source code is no longer portable. 阻止人们将编译C用作C ++设置的一个原因是因为这意味着他们的源代码不再是可移植的。 That setting is a project setting and so if a .c file is dropped into another project, it will not be compiled as c++. 该设置是项目设置,因此如果.c文件被放入另一个项目,它将不会被编译为c ++。 I would rather people take the time to rename file suffixes to .cpp. 我宁愿人们花时间将文件后缀重命名为.cpp。


  1. extern "C" doesn't change the presence or absence of the __cplusplus macro. extern "C"不会改变__cplusplus宏的存在与否。 It just changes the linkage and name-mangling of the wrapped declarations. 它只是更改包装声明的链接和名称修改。

  2. You can nest extern "C" blocks quite happily. 您可以非常愉快地嵌套extern "C"块。

  3. If you compile your .c files as C++ then anything not in an extern "C" block, and without an extern "C" prototype will be treated as a C++ function. 如果您编译.c文件作为C ++,然后什么也没在extern "C"块,并且在没有extern "C"的原型将被视为C ++函数。 If you compile them as C then of course everything will be a C function. 如果将它们编译为C,那么当然一切都将是C函数。

  4. Yes

  5. You can safely mix C and C++ in this way. 您可以通过这种方式安全地混合使用C和C ++。


extern "C" doesn't really change the way that the compiler reads the code. extern "C"并没有真正改变编译器读取代码的方式。 If your code is in a .c file, it will be compiled as C, if it is in a .cpp file, it will be compiled as C++ (unless you do something strange to your configuration). 如果你的代码在.c文件中,它将被编译为C,如果它在.cpp文件中,它将被编译为C ++(除非你对你的配置做了一些奇怪的事情)。

What extern "C" does is affect linkage. extern "C"作用是影响联系。 C++ functions, when compiled, have their names mangled -- this is what makes overloading possible. 编译时,C ++函数的名称受损 - 这就是使重载成为可能的原因。 The function name gets modified based on the types and number of parameters, so that two functions with the same name will have different symbol names. 函数名称根据参数的类型和数量进行修改,因此具有相同名称的两个函数将具有不同的符号名称。

Code inside an extern "C" is still C++ code. extern "C"中的代码仍然是C ++代码。 There are limitations on what you can do in an extern "C" block, but they're all about linkage. 你可以在extern“C”块中做些什么,但它们都是关于链接的。 You can't define any new symbols that can't be built with C linkage. 您无法定义任何无法使用C链接构建的新符号。 That means no classes or templates, for example. 例如,这意味着没有类或模板。

extern "C" blocks nest nicely. extern "C"块很好地嵌套。 There's also extern "C++" if you find yourself hopelessly trapped inside of extern "C" regions, but it isn't such a good idea from a cleanliness perspective. 如果你发现自己被困在extern "C"区域内,那么还有extern "C++" ,但从清洁角度来看,它并不是一个好主意。

Now, specifically regarding your numbered questions: 现在,特别是关于您的编号问题:

Regarding #1: __cplusplus will stay defined inside of extern "C" blocks. 关于#1:__ cplusplus将保留在extern "C"块内部。 This doesn't matter, though, since the blocks should nest neatly. 然而,这并不重要,因为块应该整齐地嵌套。

Regarding #2: __cplusplus will be defined for any compilation unit that is being run through the C++ compiler. 关于#2:将为正在通过C ++编译器运行的任何编译单元定义__cplusplus。 Generally, that means .cpp files and any files being included by that .cpp file. 通常,这意味着.cpp文件和该.cpp文件包含的任何文件。 The same .h (or .hh or .hpp or what-have-you) could be interpreted as C or C++ at different times, if different compilation units include them. 如果不同的编译单元包含它们,则相同的.h(或.hh或.hpp或what-have-you)可以在不同的时间被解释为C或C ++。 If you want the prototypes in the .h file to refer to C symbol names, then they must have extern "C" when being interpreted as C++, and they should not have extern "C" when being interpreted as C -- hence the #ifdef __cplusplus checking. 如果你希望.h文件中的原型引用C符号名称,那么当它们被解释为C ++时它们必须具有extern "C" ,并且当被解释为C时它们不应该具有extern "C" - 因此#ifdef __cplusplus检查。

To answer your question #3: functions without prototypes will have C++ linkage if they are in .cpp files and not inside of an extern "C" block. 回答你的问题#3:没有原型的函数如果在.cpp文件中而不在extern "C"块内,则会有C ++链接。 This is fine, though, because if it has no prototype, it can only be called by other functions in the same file, and then you don't generally care what the linkage looks like, because you aren't planning on having that function be called by anything outside the same compilation unit anyway. 但这很好,因为如果它没有原型,它只能被同一文件中的其他函数调用,然后你通常不关心链接是什么样的,因为你没有计划具有该功能无论如何,被同一个编译单元之外的任何东西调用。

For #4, you've got it exactly. 对于#4,你已经完全掌握了它。 If you are including a header for code that has C linkage (such as code that was compiled by a C compiler), then you must extern "C" the header -- that way you will be able to link with the library. 如果要包含具有C链接的代码的标题(例如由C编译器编译的代码),则必须在标题extern "C" - 这样您就可以与库链接。 (Otherwise, your linker would be looking for functions with names like _Z1hic when you were looking for void h(int, char) (否则,当您查找void h(int, char)时,您的链接器将查找名称如_Z1hic函数

5: This sort of mixing is a common reason to use extern "C" , and I don't see anything wrong with doing it this way -- just make sure you understand what you are doing. 5:这种混合是使用extern "C"的常见原因,我没有看到这样做的任何错误 - 只要确保你明白你在做什么。


It's about the ABI, in order to let both C and C++ application use C interfaces without any issue. 它是关于ABI的,为了让C和C ++应用程序都使用C接口而没有任何问题。

Since C language is very easy, code generation was stable for many years for different compilers, such as GCC, Borland C\\C++, MSVC etc. 由于C语言非常简单,因此对于不同的编译器,如GCC,Borland C \\ C ++,MSVC等,代码生成可以保持多年。

While C++ becomes more and more popular, a lot things must be added into the new C++ domain (for example finally the Cfront was abandoned at AT&T because C could not cover all the features it needs). 虽然C ++变得越来越流行,但是必须在新的C ++域中添加很多东西(例如,最后在AT&T放弃了Cfront,因为C无法涵盖它所需的所有功能)。 Such as template feature, and compilation-time code generation, from the past, the different compiler vendors actually did the actual implementation of C++ compiler and linker separately, the actual ABIs are not compatible at all to the C++ program at different platforms. 例如模板特性和编译时代码生成,从过去开始,不同的编译器厂商实际上分别实际执行了C ++编译器和链接器,实际的ABI根本不兼容不同平台的C ++程序。

People might still like to implement the actual program in C++ but still keep the old C interface and ABI as usual, the header file has to declare extern "C" {} , it tells the compiler generate compatible/old/simple/easy C ABI for the interface functions if the compiler is C compiler not C++ compiler. 人们可能仍然喜欢用C ++实现实际的程序,但仍像往常一样保留旧的C接口和ABI,头文件必须声明extern“C”{} ,它告诉编译器生成兼容/ old / simple / easy C ABI对于接口函数,如果编译器是C编译器而不是C ++编译器。