首页 文章

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

提问于
浏览
260

我正在开发一个包含大量遗留代码的项目 . 我们对 C 和C如何相互作用有点困惑 . 我理解通过用 extern "C" 包装 C 代码,C编译器不会破坏 C 代码's names, but I' m并不完全确定如何实现它 .

因此,在每个 C 头文件的顶部(在包含警卫之后),我们有

#ifdef __cplusplus
extern "C" {
#endif

在底部,我们写

#ifdef __cplusplus
}
#endif

在两者之间,我们拥有所有的includes,typedef和函数原型 . 我有几个问题,看看我是否理解正确:

  • 如果我有一个包含 C 头文件B.h的C文件A.h,包含另一个 C 头文件C.h,这是如何工作的?我认为当编译器进入B.h时,将定义 __cplusplus ,因此它将使用 extern "C" 包装代码(并且不会在此块中定义 __cplusplus ) . 因此,当它进入C.h时,将不会定义 __cplusplus ,并且代码将不会包含在 extern "C" 中 . 它是否正确?

  • extern "C" { extern "C" { .. } } 包装一段代码有什么问题吗?第二个_425806会做什么?

  • 我们不把这个包装器放在.c文件周围,只放在.h文件中 . 那么,如果函数没有原型会发生什么?编译器是否认为它是C函数?

  • 我们还使用了一些用 C 编写的第三方代码,并没有这种包装 . 每当我从该库中包含一个 Headers 时,我一直在#include周围放置一个 extern "C" . 这是处理这个问题的正确方法吗?

  • 最后,这是一个好主意吗?还有什么我们应该做的吗?我们将在可预见的未来混合 C 和C,我想确保我们覆盖所有基地 .

3 回答

  • 34

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

    extern "C" 所做的是影响联系 . 编译时,C函数的名称被破坏 - 这就是使重载成为可能的原因 . 函数名称根据参数的类型和数量进行修改,因此具有相同名称的两个函数将具有不同的符号名称 .

    extern "C" 内的代码仍然是C代码 . 在extern "C"块中可以执行的操作存在限制,但是它们定义了无法使用C链接构建的任何新符号 . 例如,这意味着没有类或模板 .

    extern "C" 块很好地嵌套 . 如果你发现自己被绝望地困在了 extern "C" 区域内,那么这也是 extern "C++" ,但从清洁角度看它并不是一个好主意 .

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

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

    关于#2:将为正在通过C编译器运行的任何编译单元定义__cplusplus . 通常,这意味着.cpp文件和该.cpp文件包含的任何文件 . 如果不同的编译单元包含它们,则相同的.h(或.hh或.hpp或what-have-you)可以在不同时间被解释为C或C.如果您希望.h文件中的原型引用C符号名称,那么它们在被解释为C时必须具有 extern "C" ,并且在被解释为C时它们不应该具有 extern "C" - 因此 #ifdef __cplusplus 检查 .

    回答你的问题#3:没有原型的函数如果它们在.cpp文件中而不在 extern "C" 块内,则会有C链接 . 但这很好,因为如果它没有原型,它只能被同一文件中的其他函数调用,然后你就不会计划在同一个编译单元之外的任何东西调用该函数 .

    对于#4,你已经完全掌握了它 . 如果要包含具有C链接的代码的 Headers (例如由C编译器编译的代码),则必须 extern "C" Headers - 这样您就可以与库链接 . (否则,当您查找 void h(int, char) 时,您的链接器将查找名称如 _Z1hic 的函数

    5:这种混合是使用 extern "C" 的常见原因,我没有看到这样做的任何错误 - 只要确保你明白你在做什么 .

  • 228

    一些问题是安德鲁·谢兰斯基(Andrew Shelansky)的优秀答案和不同意的一些问题并没有真正改变编译器读取代码的方式

    因为您的函数原型被编译为C,所以不能使用不同的参数重载相同的函数名称 - 这是编译器名称修改的关键特性之一 . 它被描述为一个链接问题,但事实并非如此 - 您将从中获得错误编译器和链接器 .

    如果您尝试使用原型声明的C功能(例如重载),则会出现编译器错误 .

    链接器错误将在稍后发生,因为您的函数似乎找不到,如果您执行 not 包含声明的外部"C"包装器,并且标头包含在C和C源的混合中 .

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

  • 18
    • extern "C" 不会更改 __cplusplus 宏的存在与否 . 它只是更改包装声明的链接和名称修改 .

    • 你可以非常高兴地嵌套 extern "C" 块 .

    • 如果将 .c 文件编译为C,那么 extern "C" 块中没有 extern "C" 块的任何内容都将被视为C函数 . 如果将它们编译为C,那么当然一切都将是C函数 .

    • 是的

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

相关问题