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.h
void fuck(int a);
// test.c
void 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++函数,重载了