https://www.cnblogs.com/kunhu/p/3629874.html
https://blog.csdn.net/jscese/article/details/37821961

需求场景:系统内核一般由C开发,上层应用由C++开发,上层应用需要调用内核的API,这时候出现C++调用C的需求,但是C和C++的编译不同,我们需要告知编译器哪些代码用C方式编译链接。

C /C++编译

C和C++虽然是近亲,但是编译还是有不同的,比如C++支持重载,而C不支持。

  1. void fuck(int a);
  2. void fuck(float a);
  3. // C++为了支持重载,会对编译后的函数名字做一些添加修改,如
  4. // fuck(int a)编译后的函数名字类似这个样子: fuck@@YAXHHH@Z+0x22
  5. // 这样才能支持参数不同依然能找到正确的函数。
  6. // 而C的编译结果,并没有对函数名字做什么修改,如
  7. // fuck(int a)编译后的名字类似这个样子:fuck
  8. // fuck(float a)编译后的名字也是fuck,
  9. // 假如main.cpp文件使用到了test.c中定义的函数void fuck(int a);
  10. // 则会出现链接错误,找不到函数。原因是以C的方式编译,以C++的方式链接,从上我们知道
  11. // 链接时寻找的函数名和编译的函数名是不同的。
  12. //
  13. // 代码如下;
  14. //
  15. // main.cpp
  16. #include "test.h"
  17. int main(){
  18. fuck(1);
  19. }
  20. // test.h
  21. void fuck(int a);
  22. // test.c
  23. void fuck(int a){
  24. printf(a);
  25. }

需求实现

C++的设计者为C++程序员提供了一种控制指定代码链接方式的机制(extern “C”,链接提示)。
C++使用链接指示(linkage directive)指出任意非C++函数所用的语言。最常见的是调用C语言编写的函数。
要想把C++代码和其他语言(包括C语言)编写的代码放在一起使用,要求我们必须有权访问该语言的编译器,并且这个编译器与当前的C++编译器是兼容的。
注意它不能出现在类和函数定义内部。

C++调用C

  1. // ******************************************************************************
  2. // ******** 以下例子展示C++中调用C函数funcC()。
  3. // ******** C++文件:main.cpp
  4. // ******** C文件:FuncC.h、FuncC.c
  5. // ******************************************************************************
  6. // ******************************************************************************
  7. // main.h
  8. #include "FuncC.h" // C头文件
  9. int main()
  10. {
  11. funcC( 1, 2 ); // 调用C方法,以C编译的方式链接。
  12. return 0;
  13. }
  14. // ******************************************************************************
  15. // FuncC.h
  16. #ifndef _FUNC_C_H_
  17. #define _FUNC_C_H_
  18. // extern "C" { ... } // 表示给模块内所有函数变量添加extern "C"声明
  19. #ifdef __cplusplus // 如果是C++编译器则一定会有此宏
  20. extern "C" { // 这是在告诉编译器,如果是C++编译,则这里的所有函数
  21. #endif // __cplusplus // 变量都以C编译的方式链接
  22. // 如果没有这个宏判断则会有漏洞,假如是C编译器,extern "C"将被识别为语法错误。
  23. // 这是规范的extern "C"写法,以这种方式写的C函数可以同时被C/C++调用。
  24. void funcC( const int a, const int b ); // 此函数是C函数,
  25. // 当时C++调用C时,即C++编译器编译,则会添加extern "C"声明,也就是以C方式链接此函数
  26. #ifdef __cplusplus
  27. }
  28. #endif
  29. #endif
  30. // ******************************************************************************
  31. // FuncC.c,.c文件代码是C代码,是按照C编译的。
  32. #include "FuncC.h"
  33. #include <stdio.h>
  34. void funcC( const int a, const int b )
  35. {
  36. printf( "funcC(%d, %d)", a, b );
  37. }

C调用C++

  1. // ******************************************************************************
  2. // ******** 以下例子展示C中调用C++函数FuncCpp()
  3. // ******** C++文件:FuncCpp.h、FuncCpp.cpp
  4. // ******** C文件:main.c
  5. // ******************************************************************************
  6. // ******************************************************************************
  7. // main.c
  8. #include <stdio.h>
  9. extern void funcCpp( int a, int b ); // C++函数
  10. // extern "C" void funcCpp( int a, int b ); // 错误,C编译器无法识别extern "C"
  11. int main()
  12. {
  13. funcCpp( 1, 2 ); // 调用C++函数
  14. return 0;
  15. }
  16. // ******************************************************************************
  17. // FuncCpp.h
  18. #ifndef _FUNC_CPP_H_
  19. #define _FUNC_CPP_H_
  20. // extern "C"声明以下C++函数
  21. extern "C" {
  22. void funcCpp( int a, int b );
  23. }
  24. #endif
  25. // ******************************************************************************
  26. // FuncCpp.cpp
  27. #include <iostream>
  28. #include "FuncCpp.h"
  29. void funcCpp( int a, int b )
  30. {
  31. std::cout << "funcCpp( " << a << ", " << b << ")" << std::endl;
  32. }

完整用法

C++调用类C

即在C++编译环境下。

  1. // 这是cstring头文件的某些C函数的声明。
  2. extern "C" size_t strlen(const char*); // 单语句链接指示
  3. extern "C" { // 复合语句链接指示,块中所有都加了extern "C"
  4. int strcmp(const char*, const char*);
  5. char *strcat(char*, const char*);
  6. #include<string.h> // 头文件中的所有声明都加了extern "C"
  7. }
  8. // C++可以与类C语言混合编程,比如Ada、FORTRAN,这些语言的编译器与C++编译器兼容。
  9. extern "Ada"{
  10. }
  11. extern"FORTRAN"{
  12. }

指向extern”C”函数的指针:

  1. // 编写函数所用的语言是函数类型的一部分。
  2. // pf指向一个C函数,该函数接受一个int返回void
  3. // 当调用pf时,编译器认定当前调用的是一个C函数。
  4. extern "C" void (*pf)(int);
  5. // extern C对整个声明都有效。
  6. // f1是一个c函数,它的形参是一个指向c函数的指针
  7. extern "C" void f1(void(*)(int));
  8. void (*pf1) (int); // 指向一个C++函数
  9. extern "C" void (*pf2) (int); // 指向一个C函数
  10. pf1 = pf2 ; // 错误:pf1和pf2的类型不同
  11. extern "C" typedef void FC(int); // FC是一个指向C函数的指针
  12. void f2(FC *); // f2是一个C++函数,该函数的形参是指向C函数的指针

导出C++

  1. // calc函数可以被C程序调用
  2. extern "C" double calc (double dparm) { / *... * / } // C++函数
  3. // 编译器将为该函数生成适合于指定语言的代码。
  4. // 值得注意的是,可被多种语言共享的函数的返回类型或形参类型受到很多限制。例如,
  5. // 我们不太可能把一个C++类的对象传给C程序,因为C程序根本无法理解构造函数、析构函数以及其他类特有的操作。

函数重载

链接指示与重载函数的相互作用依赖于目标语言。如果目标语言支持重载函数,则为该语言实现链接指示的编译器很可能也支持重载这些C++的函数。

  1. // C不支持重载。
  2. // 错误:两个extern "C"函数的名字相同
  3. extern "C" void print(const char*);
  4. extern "C" void print(int);
  5. class Smallint { /*. .. */ };
  6. class BigNum { /*. .. */);
  7. extern "C" double calc(double) ; // 这个是C函数版本
  8. extern Smallint calc(const Smallint&); // 这两个是C++函数,重载了
  9. extern BigNum calc(const BigNum&); // 这两个是C++函数,重载了