问答中心分类: C++结合 C++ 和 C – #ifdef __cplusplus 如何工作?
0
匿名用户 提问 10小时 前

我正在做一个有很多遗产的项目C代码。我们已经开始用 C++ 编写,目的是最终也转换遗留代码。我有点困惑如何C和 C++ 交互。我明白,通过包装C代码与extern "C"C++ 编译器不会破坏C代码的名称,但我不完全确定如何实现这一点。
所以,在每个顶部C头文件(在包含守卫之后),我们有

#ifdef __cplusplus
extern "C" {
#endif

在底部,我们写

#ifdef __cplusplus
}
#endif

在两者之间,我们拥有所有的包含、类型定义和函数原型。我有几个问题,看看我是否理解正确:

  1. 如果我有一个 C++ 文件 A.hh,其中包含一个C头文件 Bh,包括另一个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++ 在可预见的未来,我想确保我们覆盖了我们所有的基础。
Lazer 回复 10小时 前

简而言之,这是最好的解释:To ensure that the names declared in that portion of code have C linkage, and thus C++ name mangling is not performed.(我从链接)

Lazer 回复 10小时 前

有关的:在同一程序中混合 C 和 C++ 代码

3 Answers
0
Anthony Williams 回答 10小时 前
  1. extern "C"不会改变存在或不存在__cplusplus宏。它只是改变了包装声明的链接和名称修饰。
  2. 你可以嵌套extern "C"块很高兴。
  3. 如果你编译你的.c文件作为 C++ 然后任何不在extern "C"块,并且没有extern "C"原型将被视为 C++ 函数。如果将它们编译为 C,那么当然一切都是 C 函数。
  4. 是的
  5. 您可以通过这种方式安全地混合 C 和 C++。
David C. 回复 10小时 前

如果你编译.c文件作为 C++,然后一切都编译为 C++ 代码,即使它在extern "C"堵塞。这extern "C"代码不能使用依赖于 C++ 调用约定的特性(例如运算符重载),但函数的主体仍然编译为 C++,所有这些都需要。

0
Andy Dent 回答 10小时 前

有几个陷阱是 Andrew Shelansky 出色答案的共同点,但有点不同意并没有真正改变编译器读取代码的方式
因为您的函数原型被编译为 C,所以您不能使用不同的参数重载相同的函数名称 – 这是编译器名称修饰的关键特性之一。它被描述为一个链接问题,但事实并非如此——编译器和链接器都会出错。
如果您尝试使用原型声明的 C++ 功能(例如重载),则会出现编译器错误。
链接器错误将在稍后发生,因为您的函数似乎找不到,如果您这样做不是外部“C”声明的包装器和标头包含在 C 和 C++ 源代码的混合中。
阻止人们使用将 C 编译为 C++设置是因为这意味着它们的源代码不再可移植。该设置是项目设置,因此如果将 .c 文件拖放到另一个项目中,它将不会被编译为 c++。我宁愿人们花时间将文件后缀重命名为 .cpp。

Mitchell Currie 回复 10小时 前

这是一个神秘的原因,把我的头发拉了出来。确实需要张贴在某个地方。

0
Bo Zhou 回答 10小时 前

这是关于 ABI,为了让 C 和 C++ 应用程序都可以毫无问题地使用 C 接口。
由于 C 语言非常简单,对于不同的编译器,例如 GCC、Borland C\C++、MSVC 等,代码生成多年来都是稳定的。
虽然 C++ 变得越来越流行,但必须将很多东西添加到新的 C++ 域中(例如,最终 Cfront 在 AT&T 被放弃,因为 C 无法涵盖它需要的所有功能)。如模板特性,以及编译时代码生成,从过去来看,C++编译器和链接器的实际实现都是由不同的编译器厂商分开进行的,实际的ABI与不同平台的C++程序完全不兼容。
人们可能仍然喜欢用 C++ 实现实际的程序,但仍然像往常一样保留旧的 C 接口和 ABI,头文件必须声明外部“C”{},它告诉编译器为接口函数生成 compatible/old/simple/easy C ABI如果编译器是 C 编译器而不是 C++ 编译器。