C++中的extern “C”用法详解
extern "C"表明了一种编译规约,其中extern是关键字属性,“C”表征了编译器链接规范。对于extern "C"可以理解成在C++/C中的混合编程的编译指令。用“C”来规约在C++中用C的方式进行编译和链接。
extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。让我们来详细解读这两重含义。
1. 被extern"C"限定的函数或变量是extern类型的;
2. extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量是一个已经定义的外部变量或函数,有了此声明,就可以从声明处起,合法的使用该变量或函数。对于extern变量来说,仅仅是一个变量的声明,其并不是在定义分配内存空间。记住,下列语句:extern int a;仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间,类型名int可以省略不写。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。
通常,也可以在模块A的头文件中对本模块A提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。而且编译阶段,外面是找不到该函数的,但是不报错。link阶段会从定义模块生成的目标代码中找到此函数。
与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。
作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。从代码到可执行程序需要经过编译和链接两个过程,其中编译阶段会做语法检测,代码展开,另外它还会做一件事,就是将变量转成符号,链接的时候其实是通过符号来定位的。编译器在编译C和C++代码时,将变量转成符号的过程符号名称是不同的,混合编程在链接时就会出现找不到函数的现象。例如,假设某个函数的原型为:
void foo( int x, int y );
该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangledname”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y)编译生成的符号是不相同的,后者为_foo_int_float。
这样也很容易理解为什么C++支持函数重载而C不支持了,因为C++将函数修饰为符号时把函数的参数类型加进去了,而C却没有,所以在C++下,即便函数名相同,只要参数不同,它们的符号名是不会冲突的。
(1)在C++中引用C语言中的函数,需要在C++头文件里加extern “C”,否则C++编译的时候会找不到符号
/* c语言头文件:cExample.h */
1 2 3 4 |
|
/* c语言实现文件:cExample.c */
1 2 3 4 5 |
|
// c++实现文件,调用add:cppFile.cpp
1 2 3 4 5 6 7 8 9 |
|
(2)在C中引用C++语言中的函数,C++的头文件需添加extern "C",但是在C语言中不能直接引用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C"函数声明为extern类型。
1. 在C++的.h文件中用extern “C”{}声明将被C程序使用的函数
//C++头文件 cppExample.h
1 2 3 4 |
|
2. 在C++的.cpp文件中实现上面的函数
//C++实现文件 cppExample.cpp
1 2 3 4 5 |
|
3. 在.c文件中用extern声明要使用的C++函数
// C实现文件 cFile.c 注意:切不可在.c文件中包含C++的.h文件,那样编译无法通过
/* 这样会编译出错:#include "cppExample.h" */
1 2 3 4 5 6 |
|