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

9 浏览
0 Comments

结合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++,我想确保我们涵盖了所有的基础。

admin 更改状态以发布 2023年5月23日
0
0 Comments
  1. extern "C" 不会改变 __cplusplus 宏的存在与否,它只会改变被包装声明的连接方式和名称步骤。

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

  3. 如果你将你的 .c 文件编译为 C++,那么不在 extern "C" 块中,也没有 extern "C" 原型的函数都将被视为 C++ 函数。如果你将它们编译成 C,那么一切都将是 C 函数。

  4. 是的

  5. 你可以安全地混合使用 C 和 C++。

0
0 Comments

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"的常见原因,我认为这种做法是没有问题的,只要你明白自己在做什么。

0