结合C++和C——#ifdef __cplusplus如何工作?
结合C++和C——#ifdef __cplusplus如何工作?
我正在开发一个有很多传统C代码的项目。我们已经开始使用C++编写代码,并打算最终对传统代码进行转换。我对C和C++的互操作有一些困惑。我知道通过用\"extern \"C\"\"来包装C代码,C++编译器就不会破坏C代码的名称,但我不太确定如何实现这一点。
因此,在每个C头文件的顶部(包含保护之后),我们写上
#ifdef __cplusplus extern "C" { #endif
然后在底部写上
#ifdef __cplusplus } #endif
在两者之间,我们有所有的include、typedefs和函数原型。我有几个问题,想知道我是否理解正确:
1. 如果我有一个C++文件A.hh,其中包含一个C头文件B.h,又包含另一个C头文件C.h,这个怎么工作?我认为当编译器进入B.h时,“__cplusplus”会被定义,所以它会用“extern \"C\"”包装代码(这个块内不会定义__cplusplus)。所以当它进入C.h时,“__cplusplus”将不会被定义,代码也不会被包装在“extern \"C\"”中。这是正确的吗?
2. 用“extern \"C\" { extern \"C\" { .. } }”来包装代码有什么问题吗?第二个“extern \"C\"”会做什么?
3. 我们没有将这个包装器放在.c文件中,只放在了.h文件中。那如果一个函数没有原型呢?编译器会认为它是C++函数吗?
4. 我们还使用了一些第三方用C编写的代码,没有这种包装器。每次我包含该库的头文件时,我都会在#include周围加上\"extern \"C\"\"。这样做对吗?
5. 最后,这种设置好吗?我们将在可见的将来混合使用C和C++,我想确保我们涵盖了所有的基础。
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文件包含的文件。如果不同的编译单元将它们解释为C或C++,则相同的.h(.hh,.hpp或类似文件)可能会受到不同的解释。如果你想在.h文件中的原型中引用C符号名称,那么在被解释为C++时,它们必须具有extern "C"
,而当解释为C时,则不应该有extern "C"
,因此需要使用 #ifdef __cplusplus 检查。
回答您的问题#3:如果函数没有原型,它们在.cpp文件中且不在extern "C"
块中,则具有C++链接。然而,这是可以接受的,因为如果没有原型,则它只能被同一文件中的其他函数调用,那么你一般不用关心链接的样子,因为你并不打算让那个函数被同一编译单元外的东西调用。
对于#4,您的理解是正确的。如果你正在包含具有C链接的代码的头文件(例如由C编译器编译的代码),那么你必须extern "C"
头文件,这样你才能链接到库中。(否则,当您正在寻找void h(int,char)
时,链接器将查找名称为_Z1hic
的函数)
5:这种混合使用的情况是使用extern "C"
的常见原因,我认为这种做法是没有问题的,只要你明白自己在做什么。