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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3789340/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:44:43  来源:igfitidea点击:

Combining C++ and C - how does #ifdef __cplusplus work?

c++cc-preprocessorextern-c

提问by dublev

I'm working on a project that has a lot of legacy Ccode. We've started writing in C++, with the intent to eventually convert the legacy code, as well. I'm a little confused about how the Cand C++ interact. I understand that by wrapping the Ccode with extern "C"the C++ compiler will not mangle the Ccode's names, but I'm not entirely sure how to implement this.

我正在处理一个包含大量遗留C代码的项目。我们已经开始用 C++ 编写,目的是最终也转换遗留代码。我对C和 C++如何交互有点困惑。我知道通过用C++ 编译器包装C代码extern "C"不会破坏C代码的名称,但我不完全确定如何实现这一点。

So, at the top of each Cheader file (after the include guards), we have

所以,在每个C头文件的顶部(在包含保护之后),我们有

#ifdef __cplusplus
extern "C" {
#endif

and at the bottom, we write

在底部,我们写

#ifdef __cplusplus
}
#endif

In between the two, we have all of our includes, typedefs, and function prototypes. 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 Cheader file B.h, includes another Cheader file C.h, how does this work? I think that when the compiler steps into B.h, __cpluspluswill be defined, so it will wrap the code with extern "C"(and __cpluspluswill not be defined inside this block). So, when it steps into C.h, __cpluspluswill not be defined and the code will not be wrapped in extern "C". Is this correct?

  2. Is there anything wrong with wrapping a piece of code with extern "C" { extern "C" { .. } }? What will the second extern "C"do?

  3. We don't put this wrapper around the .c files, just the .h files. So, what happens if a function doesn't have a prototype? Does the compiler think that it's a C++ function?

  4. We are also using some third-party code which is written in C, and does not have this sort of wrapper around it. Any time I include a header from that library, I've been putting an extern "C"around the #include. 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 Cand C++ for the foreseeable future, and I want to make sure we're covering all our bases.

  1. 如果我有一个包含C头文件 Bh的 C++ 文件 A.hh ,包含另一个C头文件 Ch,这是如何工作的?我认为当编译器步入 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++,我想确保我们涵盖了所有基础。

采纳答案by Andrew Shelansky

extern "C"doesn't really change the way that the compiler reads the code. 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).

extern "C"并没有真正改变编译器读取代码的方式。如果您的代码在 .c 文件中,它将被编译为 C,如果它在 .cpp 文件中,它将被编译为 C++(除非您对配置做了一些奇怪的事情)。

What extern "C"does is affect linkage. C++ functions, when compiled, have their names mangled -- this is what makes overloading possible. 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.

什么extern "C"是影响链接。C++ 函数,在编译时,它们的名字被修改了——这就是重载成为可能的原因。函数名称会根据参数的类型和数量进行修改,因此具有相同名称的两个函数将具有不同的符号名称。

Code inside an extern "C"is still C++ code. There are limitations on what you can do in an extern "C" block, but they're all about linkage. You can't define any new symbols that can't be built with C linkage. That means no classes or templates, for example.

anextern "C"中的代码仍然是 C++ 代码。在 extern "C" 块中可以做什么是有限制的,但它们都是关于链接的。您不能定义任何不能用 C 链接构建的新符号。例如,这意味着没有类或模板。

extern "C"blocks nest nicely. 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++",如果你发现自己陷入绝望的内部extern "C"区域,但它不是从清洁的角度这样一个好主意。

Now, specifically regarding your numbered questions:

现在,特别是关于您编号的问题:

Regarding #1: __cplusplus will stay defined inside of extern "C"blocks. This doesn't matter, though, since the blocks should nest neatly.

关于#1:__cplusplus 将在extern "C"块内保持定义。不过这并不重要,因为块应该整齐地嵌套。

Regarding #2: __cplusplus will be defined for any compilation unit that is being run through the C++ compiler. Generally, that means .cpp files and any files being included by that .cpp file. 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. 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 __cpluspluschecking.

关于#2: __cplusplus 将为通过 C++ 编译器运行的任何编译单元定义。通常,这意味着 .cpp 文件以及该 .cpp 文件中包含的任何文件。如果不同的编译单元包含它们,则相同的 .h(或 .hh 或 .hpp 或 what-have-you)可以在不同的时间被解释为 C 或 C++。如果您希望 .h 文件中的原型引用 C 符号名称,那么它们extern "C"在被解释为 C++ 时必须具有,而extern "C"在被解释为 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. 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.

回答您的问题#3:如果没有原型的函数在 .cpp 文件中而不是在extern "C"块内,它们将具有 C++ 链接。但是,这很好,因为如果它没有原型,则只能由同一文件中的其他函数调用,然后您通常不关心链接的样子,因为您不打算拥有该函数无论如何都可以被同一编译单元之外的任何东西调用。

For #4, you've got it exactly. 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. (Otherwise, your linker would be looking for functions with names like _Z1hicwhen you were looking for void h(int, char)

对于#4,您已经完全掌握了。如果您要为具有 C 链接的代码(例如由 C 编译器编译的代码)包含一个头文件,那么您必须使用extern "C"该头文件 —— 这样您就可以与库链接。(否则,您的链接器将寻找名称与_Z1hic您查找时类似的函数void h(int, char)

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",我认为这样做没有任何问题——只要确保你明白你在做什么。

回答by Anthony Williams

  1. extern "C"doesn't change the presence or absence of the __cplusplusmacro. It just changes the linkage and name-mangling of the wrapped declarations.

  2. You can nest extern "C"blocks quite happily.

  3. If you compile your .cfiles as C++ then anything not in an extern "C"block, and without an extern "C"prototype will be treated as a C++ function. If you compile them as C then of course everything will be a C function.

  4. Yes

  5. You can safely mix C and C++ in this way.

  1. extern "C"不会改变__cplusplus宏的存在或不存在。它只是改变了包装声明的链接和名称修饰。

  2. 你可以extern "C"很愉快地嵌套块。

  3. 如果您将.c文件编译为 C++,那么任何不在extern "C"块中且没有extern "C"原型的内容都将被视为 C++ 函数。如果您将它们编译为 C,那么当然一切都将是 C 函数。

  4. 是的

  5. 您可以通过这种方式安全地混合 C 和 C++。

回答by Andy Dent

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. It is described as a linkage issue but that is not quite true - you will get errors from both the compiler and the linker.

因为您的函数原型被编译为 C,所以您不能使用不同的参数重载相同的函数名称 - 这是编译器名称重整的关键特性之一。它被描述为链接问题,但这并不完全正确 - 您将收到来自编译器和链接器的错误。

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 nothave the extern "C"wrapper around declarations and the header is included in a mixture of C and C++ source.

链接器错误后会发生,因为你的函数会出现无法找到,如果你具备的extern“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. That setting is a project setting and so if a .c file is dropped into another project, it will not be compiled as c++. I would rather people take the time to rename file suffixes to .cpp.

阻止人们使用compile C as C++设置的原因之一是因为这意味着他们的源代码不再可移植。该设置是项目设置,因此如果将 .c 文件放入另一个项目,则不会将其编译为 c++。我宁愿人们花时间将文件后缀重命名为 .cpp。

回答by Bo Zhou

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). Such as templatefeature, 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++ 变得越来越流行,许多东西必须添加到新的 C++ 领域中(例如,最终 Cfront 在 AT&T 被放弃,因为 C 无法涵盖它需要的所有功能)。比如模板特性、编译时代码生成,过去不同的编译器厂商实际上是分别对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 functionsif the compiler is C compiler not C++ compiler.

人们可能仍然喜欢在 C++ 中实现实际程序,但仍然像往常一样保留旧的 C 接口和 ABI,头文件必须声明extern "C" {},它告诉编译器生成兼容/旧/简单/简单的 C ABI如果编译器是 C 编译器而不是 C++ 编译器,则用于接口函数