结合C ++和C

我正在研究一个有很多遗留C代码的项目。 我们已经开始用C ++编写,并且最终还是要转换遗留代码。 我对C和C ++如何交互有点困惑。 我明白,通过用extern "C"包装C代码,C ++编译器不会破坏C代码的名称,但我不完全确定如何实现这一点。

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

#ifdef __cplusplus
extern "C" {
#endif

在底部,我们写

#ifdef __cplusplus
}
#endif

在这两者之间,我们拥有所有的include,typedefs和函数原型。 我有几个问题,看看我是否正确理解这一点:

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

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

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

  • 我们还使用了一些用C语言编写的第三方代码,并且没有这种类型的封装。 任何时候我从这个库中包含一个头文件,我一直在#include周围放置一个extern "C" 。 这是对付这个问题的正确方法吗?

  • 最后,这是一个好主意吗? 还有什么我们应该做的? 我们将在可预见的将来混合使用C和C ++,并且我想确保我们覆盖了所有的基础。


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

    extern "C"影响联动。 C ++函数在编译时,名称会被破坏 - 这是使得重载成为可能的原因。 函数名称根据参数的类型和数量进行修改,以使两个具有相同名称的函数具有不同的符号名称。

    extern "C"代码extern "C"仍然是C ++代码。 你可以在extern“C”块中做什么,但是它们都是关于链接的。 您无法定义任何无法通过C链接创建的新符号。 例如,这意味着没有类或模板。

    extern "C"块很好地嵌套。 如果你发现自己陷入了extern "C"区域内,那么也有extern "C++" ,但从清洁的角度来看,这不是一个好主意。

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

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

    关于#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链接的代码(例如由C编译器编译的代码)包含头文件,那么您必须在头文件的extern "C"插入 - 这样您才能够与库链接。 (否则,当你寻找void h(int, char)时,你的链接器会寻找名称为_Z1hic函数,

    5:这种混合是使用extern "C"的常见理由,我没有发现这样做有什么问题 - 只要确保你明白你在做什么。


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

  • 你可以非常愉快地嵌套extern "C"块。

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

  • 您可以以这种方式安全地混合使用C和C ++。


  • 一些陷阱是Andrew Shelansky的出色答案,并且有点不同意并不能真正改变编译器读取代码的方式

    因为你的函数原型被编译为C,所以你不能用相同的参数重载相同的函数名 - 这是编译器名称重组的一个关键特性。 它被描述为一个链接问题,但这不完全正确 - 你将从编译器和链接器中获得错误。

    如果您尝试使用原型声明的C ++特性(如重载),则编译器错误将会发生。

    链接器错误将在稍后发生,因为如果您的声明周围没有外部“C”包装,并且标头包含在C和C ++源代码的混合中,那么您的函数似乎找不到。

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

    链接地址: http://www.djcxy.com/p/29813.html

    上一篇: Combining C++ and C

    下一篇: What's the benefit of using extend.js?