extern
该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。
为了使它们遵守统一规则,可以使用extern指定一个编译和连接规约。例如,声明C和C++标准库函数strcyp(),并指定它应该根据C的编译和连接规约来链接: ?1 extern "C" char* strcpy(char*,const char*); 注意它与下面的声明的不同之处: ?1 extern char* strcpy(char*,const char*); 下面的这个声明仅表示在连接的时候调用strcpy()。
如果你有很多语言要加上extern "C",你可以将它们放到extern "C"{ }中。
extern "C"指令非常有用,因为C和C++的近亲关系。注意:extern "C"指令中的C,表示的一种编译和连接规约,而不是一种语言。C表示符合C语言的编译和连接规约的任何语言,如Fortran、assembler等。
C++的编译和连接 C++是一个面向对象语言(虽不是纯粹的面向对象语言),它支持函数的重载,重载这个特性给我们带来了很大的便利。为了支持函数重载的这个特性,C++编译器实际上将下面这些重载函数: ?1234 void print(int i); void print(char c); void print(float f); void print(char* s); 编译为: ?1234 _print_int _print_char _print_float _pirnt_string 这样的函数名,来唯一标识每个函数。注:不同的编译器实现可能不一样,但是都是利用这种机制。
C的编译和连接 C语言中并没有重载和类这些特性,故并不像C++那样print(int i),会被编译为_print_int,而是直接编译为_print等。因此如果直接在C++中调用C的函数会失败,因为连接是调用C中的print(3)时,它会去找_print_int(3)。因此extern "C"的作用就体现出来了。
extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。(由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名。)
这个功能十分有用处,因为在C++出现以前,很多代码都是C语言写的,而且很底层的库也是C语言写的,为了更好的支持原来的C代码和已经写好的C语言库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。
这个功能主要用在下面的情况:
1、C++代码调用C语言代码
2、在C++的头文件中使用
3、在多个人协同开发时,可能有的人比较擅长C语言,而有的人擅长C++,这样的情况下也会有用到
给出一个我设计的例子:
moduleA、moduleB两个模块,B调用A中的代码,其中A是用C语言实现的,而B是利用C++实现的,下面给出一种实现方法:
//moduleA头文件
///对于模块A来说,这个宏是为了防止头文件的重复引用 具体的:
编译时第一次看到moduleA头文件,它的内容会被读取且给定__MODULE_A_H一个值。之后再次看到moduleA头文件时,__MODULE_A_H就已经定义了,moduleA的内容就不会再次被读取了。
#ifndef__MODULE_A_H
#define__MODULE_A_H
int fun(int, int);
#endif
///
c语言里所有以#开头的都是预编译指令,就是在正式编译之前,让编译器做一些预处理的工作。
#if和#endif是配对的,叫做条件编译指令,如果满足#if后面的条件,就编译#if和#endif之间的程序段,否则不编译。
///
//moduleA实现文件moduleA.C//模块A的实现部分并没有改变
#include"moduleA"
int fun(int a, int b)
{
return a+b;
}
//moduleB头文件
#idndef __MODULE_B_H
#define __MODULE_B_H
//而这一部分就是告诉编译器,如果定义了__cplusplus(即如果是cpp文件, //因为cpp文件默认定义了该宏),则采用C语言方式进行编译
#ifdef __cplusplus
extern "C"{
#include"moduleA.h"
#endif
… //其他代码
#ifdef __cplusplus //这个写在头文件末尾
}
#endif
#endif
//moduleB实现文件 moduleB.cpp //B模块的实现也没有改变,只是头文件的设计变化了
#include"moduleB.h"
int main()
{
cout<<fun(2,3)<<endl;
}
下面是详细的介绍:
由于C、C++编译器对函数的编译处理是不完全相同的,尤其对于C++来说,支持函数的重载,编译后的函数一般是以函数名和形参类型来命名的。
例如函数void fun(int, int),编译后的可能是(不同编译器结果不同)_fun_int_int(不同编译器可能不同,但都采用了类似的机制,用函数名和参数类型来命名编译后的函数名);而C语言没有类似的重载机制,一般是利用函数名来指明编译后的函数名的,对应上面的函数可能会是_fun这样的名字。
//
实际上,在链接阶段,连接器会从模块A生成的目标文件moduleA.obj中找_foo_int_int这样的符号!!!,显然这是不可能找到的,因为foo()函数被编译成了_foo的符号,因此会出现链接错误。
///
extern "C"包含双重含义,从字面上可以知道,首先,被它修饰的目标是"extern"的;其次,被它修饰的目标代码是"C"的。
- 被extern "C"限定的函数或变量是extern类型的
extern是C/C++语言中表明函数和全局变量的作用范围的关键字,该关键字告诉编译器,其申明的函数和变量可以在本模块或其他模块中使用。
记住,下面的语句:
extern int a; 仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配空间
通常来说,在模块的头文件中对本模块提供给其他模块引用的函数和全局变量以关键字extern生命。例如,如果模块B要引用模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但并不会报错;它会在链接阶段从模块A编译生成的目标代码中找到该函数。
extern对应的关键字是static,static表明变量或者函数只能在本模块中使用,因此,被static修饰的变量或者函数不可能被extern C修饰。
(-> static(c中定义的关键字1、被其修饰者 外部无法使用(相反的都可以使用extern在外部使用) 2、函数局部变量使用成为静态的放入静态池-生存期改变。c++进行了扩充,使它在类中有了特殊作用))
- 被extern "C"修饰的变量和函数是按照C语言方式进行编译和链接的:这点很重要!!!!
extern "C"的使用要点
1. 可以是单一语句
extern "C" double sqrt(double); // 引用外部变量函数sqrt 类型为“C” 编译器会对函数按c方式编译
2. 可以是复合语句, 相当于复合语句中的声明都加了extern "C"
extern "C"
{
double sqrt(double);
int min(int, int);
}
3.可以包含头文件,相当于头文件中的声明都加了extern "C"
extern "C"
{
#i nclude <cmath>
}
4. 不可以将extern "C" 添加在函数内部
5. 如果函数有多个声明,可以都加extern "C", 也可以只出现在第一次声明中,后面的声明会接受第一个链接指示符的规则。
6. 除extern "C", 还有extern "FORTRAN" 等。