代码之家  ›  专栏  ›  技术社区  ›  dublev

C++与C的结合——如何实现?

  •  281
  • dublev  · 技术社区  · 14 年前

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

    所以,在每个 C 头文件(包括警卫之后),我们有

    #ifdef __cplusplus
    extern "C" {
    #endif
    

    在底部,我们写

    #ifdef __cplusplus
    }
    #endif
    

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

    1. 如果我有一个C++文件A.hh 包括 C 头文件b.h, 包括另一个 C 头文件c.h, 这是怎么工作的?我觉得 当编译器进入b.h时, __cplusplus 将被定义,因此 将用包装代码 外部“C” (和) _ CPlusplus公司 不会 在此块中定义)。所以, 当它进入C.H. _ CPlusplus公司 不会被定义 代码不会被包装在 外部“C” . 这是正确的吗?

    2. 有什么问题吗 用包装一段代码 extern "C" { extern "C" { .. } } ? 第二个是什么 外部“C” 怎么办?

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

    4. 我们也在使用一些第三方 写入的代码 C ,确实如此 没有这种包装 它。任何时候我包括一个标题 从图书馆,我一直在 安 外部“C” 关于包括。 这是处理问题的正确方法吗 那?

    5. 最后,这是一个好主意吗? 我们还有什么要做的吗? 我们要混合 C C++ 在可预见的未来,我 要确保我们覆盖了所有 我们的基地。

    4 回复  |  直到 6 年前
        1
  •  246
  •   VLL Mohamed El-Nakeep    6 年前

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

    什么 外部“C” 是否影响连杆。编译时,C++函数的名称会被损坏——这就是使重载成为可能的原因。函数名会根据参数的类型和数量进行修改,这样两个同名的函数将具有不同的符号名。

    代码内部 外部“C” 仍然是C++代码。在外部的“c”块中可以做什么是有限制的,但它们都是关于链接的。不能定义任何不能用C链接生成的新符号。例如,这意味着没有类或模板。

    外部“C” 积木筑巢得很好。还有 extern "C++" 如果你发现自己被困在 外部“C” 但从清洁的角度来看,这不是一个好主意。

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

    关于1:35; cplusplus将在 外部“C” 阻碍。不过,这并不重要,因为这些石块应该整齐地筑巢。

    关于α2:对于编译通过C++编译器运行的任何编译单元,将定义通常,这意味着.cpp文件和该.cpp文件包含的任何文件。相同的。如果不同的编译单元包含H(OR,H.O.HPP或你们有什么),它们可以在不同的时间被解释为C或C++。如果希望.h文件中的原型引用C符号名,则它们必须 外部“C” 当被解释为C++时,它们就不应该有 外部“C” 当被解释为C时——因此 #ifdef __cplusplus 检查。

    为了回答你的问题,3:没有原型的函数如果在.CPP文件中,而不是在一个 外部“C” 阻止。不过,这很好,因为如果它没有原型,那么它只能由同一文件中的其他函数调用,然后您通常不关心链接是什么样子的,因为无论如何,您不打算让同一编译单元之外的任何对象调用该函数。

    对于4,你已经完全掌握了。如果包含具有C链接的代码的头(例如由C编译器编译的代码),则必须 外部“C” 头部——这样您就可以链接到库了。(否则,链接器将查找名称如下的函数 _Z1hic 当你在找的时候 void h(int, char)

    5:这种混合是使用的常见原因 外部“C” 我不认为这样做有什么问题——只要确保你明白你在做什么。

        2
  •  37
  •   Martin G    9 年前
    1. extern "C" 不会改变 __cplusplus 宏。它只是更改了包装声明的链接和名称管理。

    2. 你可以筑巢 外部“C” 很高兴能阻止。

    3. 如果您编译 .c 文件作为C++,那么任何东西都不在 外部“C” 块,不带 外部“C” 原型将被视为一个C++函数。如果您将它们编译为C,那么当然所有的东西都将是C函数。

    4. 是的

    5. 你可以用这种方式安全地混合C和C++。

        3
  •  20
  •   Andy Dent    11 年前

    两个戈查斯是安德鲁·谢尔兰斯基优秀答案的讨论对象,他们有点不同意 不会真正改变编译器读取代码的方式

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

    编译器错误将是如果你尝试使用C++特性的原型声明,比如重载。

    链接器错误将在稍后发生,因为如果您这样做,您的函数似乎找不到。 拥有 外部“C” 围绕报文包装,报头包含在C和C++源的混合中。

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

        4
  •  2
  •   Bo Zhou    6 年前

    它是关于ABI的,为了让C和C++应用都使用C接口而没有任何问题。

    由于C语言非常简单,对于不同的编译器,如GCC、Borland C++、MSVC等,代码生成稳定多年。

    当C++变得越来越流行时,必须在新的C++域中添加很多东西(例如,由于c++不能覆盖它所需要的所有特征,所以cAdvin在AT&AMT上被放弃了)。例如 模板 特点和编译时代码生成,从过去,不同的编译器厂商实际完成了C++编译器和链接器的实际实现,实际的ABIS根本不兼容于不同平台上的C++程序。

    人们可能仍然喜欢在C++中实现实际的程序,但仍然像以前一样保持旧的C接口和ABI,头文件必须声明。 外部“C” ,它告诉编译器 为接口函数生成兼容的/old/simple/easy c abi 如果编译器是C编译器,而不是C++编译器。

    推荐文章