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不支持。
void fuck(int a);void fuck(float a);// C++为了支持重载,会对编译后的函数名字做一些添加修改,如// fuck(int a)编译后的函数名字类似这个样子: fuck@@YAXHHH@Z+0x22// 这样才能支持参数不同依然能找到正确的函数。// 而C的编译结果,并没有对函数名字做什么修改,如// fuck(int a)编译后的名字类似这个样子:fuck// fuck(float a)编译后的名字也是fuck,// 假如main.cpp文件使用到了test.c中定义的函数void fuck(int a);// 则会出现链接错误,找不到函数。原因是以C的方式编译,以C++的方式链接,从上我们知道// 链接时寻找的函数名和编译的函数名是不同的。//// 代码如下;//// main.cpp#include "test.h"int main(){fuck(1);}// test.hvoid fuck(int a);// test.cvoid fuck(int a){printf(a);}
需求实现
C++的设计者为C++程序员提供了一种控制指定代码链接方式的机制(extern “C”,链接提示)。
C++使用链接指示(linkage directive)指出任意非C++函数所用的语言。最常见的是调用C语言编写的函数。
要想把C++代码和其他语言(包括C语言)编写的代码放在一起使用,要求我们必须有权访问该语言的编译器,并且这个编译器与当前的C++编译器是兼容的。
注意它不能出现在类和函数定义内部。
C++调用C
// ******************************************************************************// ******** 以下例子展示C++中调用C函数funcC()。// ******** C++文件:main.cpp// ******** C文件:FuncC.h、FuncC.c// ******************************************************************************// ******************************************************************************// main.h#include "FuncC.h" // C头文件int main(){funcC( 1, 2 ); // 调用C方法,以C编译的方式链接。return 0;}// ******************************************************************************// FuncC.h#ifndef _FUNC_C_H_#define _FUNC_C_H_// extern "C" { ... } // 表示给模块内所有函数变量添加extern "C"声明#ifdef __cplusplus // 如果是C++编译器则一定会有此宏extern "C" { // 这是在告诉编译器,如果是C++编译,则这里的所有函数#endif // __cplusplus // 变量都以C编译的方式链接// 如果没有这个宏判断则会有漏洞,假如是C编译器,extern "C"将被识别为语法错误。// 这是规范的extern "C"写法,以这种方式写的C函数可以同时被C/C++调用。void funcC( const int a, const int b ); // 此函数是C函数,// 当时C++调用C时,即C++编译器编译,则会添加extern "C"声明,也就是以C方式链接此函数#ifdef __cplusplus}#endif#endif// ******************************************************************************// FuncC.c,.c文件代码是C代码,是按照C编译的。#include "FuncC.h"#include <stdio.h>void funcC( const int a, const int b ){printf( "funcC(%d, %d)", a, b );}
C调用C++
// ******************************************************************************// ******** 以下例子展示C中调用C++函数FuncCpp()// ******** C++文件:FuncCpp.h、FuncCpp.cpp// ******** C文件:main.c// ******************************************************************************// ******************************************************************************// main.c#include <stdio.h>extern void funcCpp( int a, int b ); // C++函数// extern "C" void funcCpp( int a, int b ); // 错误,C编译器无法识别extern "C"int main(){funcCpp( 1, 2 ); // 调用C++函数return 0;}// ******************************************************************************// FuncCpp.h#ifndef _FUNC_CPP_H_#define _FUNC_CPP_H_// extern "C"声明以下C++函数extern "C" {void funcCpp( int a, int b );}#endif// ******************************************************************************// FuncCpp.cpp#include <iostream>#include "FuncCpp.h"void funcCpp( int a, int b ){std::cout << "funcCpp( " << a << ", " << b << ")" << std::endl;}
完整用法
C++调用类C
即在C++编译环境下。
// 这是cstring头文件的某些C函数的声明。extern "C" size_t strlen(const char*); // 单语句链接指示extern "C" { // 复合语句链接指示,块中所有都加了extern "C"int strcmp(const char*, const char*);char *strcat(char*, const char*);#include<string.h> // 头文件中的所有声明都加了extern "C"}// C++可以与类C语言混合编程,比如Ada、FORTRAN,这些语言的编译器与C++编译器兼容。extern "Ada"{}extern"FORTRAN"{}
指向extern”C”函数的指针:
// 编写函数所用的语言是函数类型的一部分。// pf指向一个C函数,该函数接受一个int返回void// 当调用pf时,编译器认定当前调用的是一个C函数。extern "C" void (*pf)(int);// extern C对整个声明都有效。// f1是一个c函数,它的形参是一个指向c函数的指针extern "C" void f1(void(*)(int));void (*pf1) (int); // 指向一个C++函数extern "C" void (*pf2) (int); // 指向一个C函数pf1 = pf2 ; // 错误:pf1和pf2的类型不同extern "C" typedef void FC(int); // FC是一个指向C函数的指针void f2(FC *); // f2是一个C++函数,该函数的形参是指向C函数的指针
导出C++
// calc函数可以被C程序调用extern "C" double calc (double dparm) { / *... * / } // C++函数// 编译器将为该函数生成适合于指定语言的代码。// 值得注意的是,可被多种语言共享的函数的返回类型或形参类型受到很多限制。例如,// 我们不太可能把一个C++类的对象传给C程序,因为C程序根本无法理解构造函数、析构函数以及其他类特有的操作。
函数重载
链接指示与重载函数的相互作用依赖于目标语言。如果目标语言支持重载函数,则为该语言实现链接指示的编译器很可能也支持重载这些C++的函数。
// C不支持重载。// 错误:两个extern "C"函数的名字相同extern "C" void print(const char*);extern "C" void print(int);class Smallint { /*. .. */ };class BigNum { /*. .. */);extern "C" double calc(double) ; // 这个是C函数版本extern Smallint calc(const Smallint&); // 这两个是C++函数,重载了extern BigNum calc(const BigNum&); // 这两个是C++函数,重载了
