结合 C++ 和 C - #ifdef __cplusplus 如何工作? [英] Combining C++ and C - how does #ifdef __cplusplus work?

查看:33
本文介绍了结合 C++ 和 C - #ifdef __cplusplus 如何工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理一个包含大量遗留 C 代码的项目.我们已经开始用 C++ 编写,目的是最终也转换遗留代码.我对 C 和 C++ 如何交互有点困惑.我知道通过用 extern "C" 包装 C 代码,C++ 编译器不会破坏 C 代码的名称,但我不是完全确定如何实现这一点.

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

#ifdef __cplusplus外部C"{#万一

在底部,我们写

#ifdef __cplusplus}#万一

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

  1. 如果我有一个 C++ 文件 A.hh包括一个 C 头文件 B.h,包含另一个 C 头文件 C.h,这是如何运作的?我觉得当编译器进入 B.h 时,__cplusplus 将被定义,所以它将用 extern "C" 包装代码(并且 __cplusplus 不会在此块内定义).所以,当它进入C.h时,__cplusplus 不会被定义并且代码不会被包裹在外部C".这是正确的吗?

  2. 有什么问题吗用一段代码包裹extern "C" { extern "C" { .. } }?第二个 extern "C"做什么?

  3. 我们不会将这个包装器放在 .c 文件中,而只是放在 .h 文件中.那么,如果一个函数没有原型会怎样呢?编译器是否认为它是一个 C++ 函数?

  4. 我们也在使用一些第三方用 C 编写的代码,并且周围没有这种包装它.任何时候我包含一个标题从那个图书馆,我一直把#include 周围的 extern "C".这是正确的处理方式吗那个?

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

解决方案

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 文件中包含的任何文件.如果不同的编译单元包含它们,则相同的 .h(或 .hh 或 .hpp 或 what-have-you)可以在不同的时间被解释为 C 或 C++.如果你想让.h文件中的原型引用C符号名称,那么它们在被解释为C++时必须有extern "C",而不应该有extern "C" 当被解释为 C 时——因此 #ifdef __cplusplus 检查.

回答您的问题#3:如果没有原型的函数在 .cpp 文件中而不是在 extern "C" 块内,它们将具有 C++ 链接.不过,这很好,因为如果它没有原型,则只能由同一文件中的其他函数调用,然后您通常不关心链接的样子,因为您不打算拥有该函数无论如何都会被同一编译单元之外的任何东西调用.

对于#4,您已经完全掌握了.如果您要为具有 C 链接的代码(例如由 C 编译器编译的代码)包含一个头文件,那么您必须 extern "C" 头文件 -- 这样您就可以与图书馆的链接.(否则,当您查找 void h(int, char)

时,您的链接器将查找名称类似于 _Z1hic 的函数

5:这种混合是使用 extern "C" 的常见原因,我认为这样做没有任何问题——只要确保你明白你是什么

I'm working on a project that has a lot of legacy C code. We've started writing in C++, with the intent to eventually convert the legacy code, as well. I'm a little confused about how the C and C++ interact. I understand that by wrapping the C code with extern "C" the C++ compiler will not mangle the C code's names, but I'm not entirely sure how to implement this.

So, at the top of each C header file (after the include guards), we have

#ifdef __cplusplus
extern "C" {
#endif

and at the bottom, we write

#ifdef __cplusplus
}
#endif

In between the two, we have all of our includes, typedefs, and function prototypes. I have a few questions, to see if I'm understanding this correctly:

  1. If I have a C++ file A.hh which includes a C header file B.h, includes another C header file C.h, how does this work? I think that when the compiler steps into B.h, __cplusplus will be defined, so it will wrap the code with extern "C" (and __cplusplus will not be defined inside this block). So, when it steps into C.h, __cplusplus will not be defined and the code will not be wrapped in extern "C". Is this correct?

  2. Is there anything wrong with wrapping a piece of code with extern "C" { extern "C" { .. } }? What will the second extern "C" do?

  3. We don't put this wrapper around the .c files, just the .h files. So, what happens if a function doesn't have a prototype? Does the compiler think that it's a C++ function?

  4. We are also using some third-party code which is written in C, and does not have this sort of wrapper around it. Any time I include a header from that library, I've been putting an extern "C" around the #include. Is this the right way to deal with that?

  5. Finally, is this set up a good idea? Is there anything else we should do? We're going to be mixing C and C++ for the foreseeable future, and I want to make sure we're covering all our bases.

解决方案

extern "C" doesn't really change the way that the compiler reads the code. If your code is in a .c file, it will be compiled as C, if it is in a .cpp file, it will be compiled as C++ (unless you do something strange to your configuration).

What extern "C" does is affect linkage. C++ functions, when compiled, have their names mangled -- this is what makes overloading possible. The function name gets modified based on the types and number of parameters, so that two functions with the same name will have different symbol names.

Code inside an extern "C" is still C++ code. There are limitations on what you can do in an extern "C" block, but they're all about linkage. You can't define any new symbols that can't be built with C linkage. That means no classes or templates, for example.

extern "C" blocks nest nicely. There's also extern "C++" if you find yourself hopelessly trapped inside of extern "C" regions, but it isn't such a good idea from a cleanliness perspective.

Now, specifically regarding your numbered questions:

Regarding #1: __cplusplus will stay defined inside of extern "C" blocks. This doesn't matter, though, since the blocks should nest neatly.

Regarding #2: __cplusplus will be defined for any compilation unit that is being run through the C++ compiler. Generally, that means .cpp files and any files being included by that .cpp file. The same .h (or .hh or .hpp or what-have-you) could be interpreted as C or C++ at different times, if different compilation units include them. If you want the prototypes in the .h file to refer to C symbol names, then they must have extern "C" when being interpreted as C++, and they should not have extern "C" when being interpreted as C -- hence the #ifdef __cplusplus checking.

To answer your question #3: functions without prototypes will have C++ linkage if they are in .cpp files and not inside of an extern "C" block. This is fine, though, because if it has no prototype, it can only be called by other functions in the same file, and then you don't generally care what the linkage looks like, because you aren't planning on having that function be called by anything outside the same compilation unit anyway.

For #4, you've got it exactly. If you are including a header for code that has C linkage (such as code that was compiled by a C compiler), then you must extern "C" the header -- that way you will be able to link with the library. (Otherwise, your linker would be looking for functions with names like _Z1hic when you were looking for void h(int, char)

5: This sort of mixing is a common reason to use extern "C", and I don't see anything wrong with doing it this way -- just make sure you understand what you are doing.

这篇关于结合 C++ 和 C - #ifdef __cplusplus 如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆