引言
由于不同的代码互相调用起来很容易出错,甚至同一种代码但由不同的编译器编译,为实现C++代码调用其他C语言代码,会在C语言代码的部分加上extern "C",表明这段代码需要按照类C的编译和连接规约来编译和连接,而不是C++的编译的连接规约。
extern "C"中的"C"指的不是一种语言,而是一种编译和链接的规约,表示符合C语言的编译和连接规约的任何语言,如Fortran、assembler等。所以并不能改变其语义,在编写被加上extern "C"的代码时还是要遵守C++的类型检测和参数转换规则。
该文章从以下方面解释extern "C"用法:
- 1、extern "C"双重含义
- 1.1、 extern
- 1.2、"C"
- 2、C与C++的相互调用
- 2.1、 C++ 调 C
- 2.2、C调 C++
1、extern "C"双重含义
extern "C" 包含双重含义,从字面上可见得它有两个部分,"extern"和"C"。
1.1、 extern
全局函数、变量等在所有的源文件是一致的,只能被定义一次,但可以被声明多次:
//sample1.c
int a = ;
//sample2.c
extern int a;
在以上代码sample2.c中使用的 a 是定义在sample1.c里的。变量 a 在所有文件中只能被定义一次否则出错,但可以被声明多次,每次声明要保持类型一致。sample2.c中的 extern 表示 a 变量在此仅仅是一个声明,而不是定义。
extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。
与extern对应的关键字是 static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。
1.2、 "C"
被extern "C"修饰的变量和函数是按照类C语言方式编译和链接的。
C++作为一种面向对象的语言,C++支持函数重载,需要在函数名字中加入函数参数,而过程式语言C则不支持,所以函数名不会被更改。例如,当函数原型为:
void func ( int a , float b );
该函数被 C 编译器编译后在符号库中的名字为 _func,而C++编译器则将函数名中加入函数参数,变为 _func_int_float 。
同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。
2、C与C++的相互调用
2.1、 C++ 调 C
C++中调用的C语言包含在C语言头文件时,可如此调用:
extern "C" {#include "sample_c.h"}
完整例子如下:
//c语言头文件:sample_c.h
#ifndef SAMPLE_C_H
#define SAMPLE_C_H
extern int plus( int a, int b);
#endif //c语言实现文件:sample_c.c
#include "sample_c.h"
int plus( int a, int b){
return a+b;
} //c++文件:sample_cpp.cpp
extern "C"{
#include "sample_c.h"
}
int main( int argc, int **argv){
plus( , );
}
2.2、 C 调 C++
C语言中不支持extern "C"声明,所以调用起C++来和C++调用C有点区别。
//C++头文件:sample_cpp.h
#ifndef SAMPLE_CPP_H
#define SAMPLE_CPP_H
extern "C" int plus( int a, int b);
#endif //C++实现文件:sample_cpp.cpp
#include "sample_cpp.h"
int plus( int a, int b){
return a+b;
} //C实现文件:sample_c.c
extern int plus( int a ,int b);//C语言中在此写#include "sample_cpp.h"会出错
int main(){
plus( , );
}