混合使用C和C++


最近在看项目代码,经常看到header file中,开头:

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

结尾:

#ifdef __cplusplus
}
#endif /* __cplusplus */

中间包裹: includestypedefs,以及function prototypes
不懂就要问,去查了下,发现这是为了告知编译器,以C语言的方式编译代码。extern "C"表示编译生成的内部符号名使用C约定。

总述

  • C语言和C++语言现在分家了,有些C语言的功能是C++不具备的。所以最好的方式就是C代码以C编译,C++代码以C++编译。1
  • extern "C"并不真的改变编译器读取代码的方式。如果你的代码在.c文件中,那么编译器就以C方式编译,如果在.cpp文件中就以C++方式编译,除非你对编译器做了什么奇怪配置。
  • extern "C"影响的是链接过程。C++支持函数重载,而C不支持,两者的编译规则也不一样。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为: void foo( int x, int y ); 该函数被C编译器编译后在符号库中的名字可能为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不 同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。2关于具体的生成名字可以通过nm命令来查看,如:nm -a foo.o。关于nm命令自行搜索。
  • extern "C"包裹的代码仍然是C++代码。在这个包裹的代码块中你能做什么是有限制的,但只是发生在链接过程。你不能定义不能在C链接过程中定义的符号(symbols),例如:classes、templates。
    也存在extern "C++",但是最好别嵌套两者。

问题

不过在StackOverflow上有人提问了几个挺好的问题。3

  1. 如果C++头文件A.hh,include 一个C头文件B.h,B.h中include C.h,这是如何工作的?提问者认为:编译器进入B.h时,__cplusplus已经定义过了,所以会包裹上extern "C",这样就相当于取消定义了__cplusplus,但是进入C.h时,这些将不会被包裹入extern "C"
    【答】 __cplusplus仍存在于extern "C"块中,但是不影响。
  2. extern "C" { extern "C" { .. } }这样的代码是错误的吗? 第二个extern "C"做了什么?
    【答】 嵌套使用是可以的。__cplusplus在使用C++编译器时是定义在每一个编译单元的。通常,这意味着__cplusplus存在于.cpp文件和任何被.cpp文件include的文件。如果不同的编译但与包含了同一个.h/.hh/.hpp文件可以,那么在不同时期它们会被解释为C或C++代码。如果你想.h文件中的原型被解译为C的符号(symbols),那么它们应该在C中没有extern "C",在C++中则有extern "C",所以需要#ifdef __cplusplus检查。
  3. 我们只是使用在了.h文件中,而非.c文件中。那么如果一个没有原型的函数会被编译器认为是C++的函数吗?
    【答】 .cpp文件中,没有原型并且不在extern "C"块中的函数,将会由C++链接。因为它们没有原型,它们只能在本文件中调用,通常你也无须关心链接过程,所以这就可以了。
  4. 提问者也在使用一些第三方的C代码,并且没有使用extern "C"包裹,那么每次include一个头文件,都自己使用extern "C"包裹起来是正确的处理方式吗?
    【答】 你的处理方法是正确的。include一个需要C链接过程的头文件,你必须使用extern "C"包含这个头文件,这样你才能和库文件链接。
  5. 最后,混合使用C和C++还需要注意什么?
    【答】 没什么了。可以这样混合使用C和C++。

使用

常见方式

当我们想从C++中调用C的库时,不能仅仅说明是一个外部函数,因为调用C函数的编译代码和调用C++函数的编译代码是不同的。如果你仅说明一个外部函数, C++编译器假定它是C++的函数,编译成功了,但当你连接时会发现错误。解决的方法就是指定它为C函数:

extern "C" // 后跟函数描述

指定一群函数的话:

extern "C"{
// 函数描述
}

如果想C和C++混用的话:

#ifdef __cplusplus
extern "C"{
#endif
// 函数描述
#ifdef __cplusplus
}
#endif

参考1推荐方式

参考11认为上述方式在header file中太丑陋了,所以它推荐把extern "C"用在.cpp文件中。

//main.cpp
extern "C" {
  #include "foo.h"
}
int main() {
  foo(22);
}

这样不会在.h文件中出现多次嵌套,影响美观。。。而且不会出现忘记包裹而出现链接错误。

Legacy

这两个是我还没看呢。

  1. include c hdrs system | Standard C++
  2. Call a C function from C++ code

参考


  1. Calling C Code from C++ With ‘extern “C”‘ ↩︎ ↩︎

  2. C++中的Name Mangling ↩︎

  3. Combining C++ and C - how does #ifdef __cplusplus work?
    ↩︎

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值