extern"C"包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。让我们来详细解读这两重含义。
(1) 被 extern限定的 函数或 变量是 extern类型的:
a. extern修饰 变量的声明。 举例来说,如果文件a.c需要引用b.c中 变量int v,就可以在a.c中声明 externint v,然后就可以 引用变量v。这里需要注意的是,被引用的 变量v的链接属性必须是外链接( external)的,也就是说a.c要引用到v,不只是取决于在a.c中声明 externint v,还取决于 变量v本身是能够被引用到的。这涉及到 c语言的另外一个话题-- 变量的 作用域。能够被其他模块以extern 修饰符引用到的变量通常是 全局变量。还有很重要的一点是, externint v可以放在a.c中的任何地方,比如你可以在a.c中的 函数fun定义的开头处声明extern int v,然后就可以引用到 变量v了,只不过这样只能在 函数fun 作用域中引用v罢了,这还是变量作用域的问题。对于这一点来说,很多人使用的时候都心存顾虑。好像 extern声明只能用于文件 作用域似的。
b. extern修饰 函数声明。从本质上来讲, 变量和函数没有区别。 函数名是指向函数二进制块开头处的 指针。如果文件a.c需要引用b.c中的 函数,比如在b.c中原型是int fun(int mu),那么就可以在a.c中声明extern int fun(int mu),然后就能使用fun来做任何事情。就像 变量的声明一样, externint fun(int mu)可以放在a.c中任何地方,而不一定非要放在a.c的文件 作用域的范围中。对其他模块中 函数的引用,最常用的方法是包含这些函数声明的头文件。使用 extern和包含头文件来引用 函数有什么区别呢?extern的引用方式比包含头文件要简洁得多!extern的使用方法是直截了当的,想引用哪个函数就用extern声明哪个函数。这大概是KISS原则的一种体现吧!这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间。在大型C 程序编译过程中,这种差异是非常明显的。
(2) 被extern "C"修饰的变量和 函数是按照C语言方式编译和连接的;
未加 extern“C”声明时的 编译方式。
首先看看C++中对类似C的 函数是怎样编译的。
作为一种 面向对象的语言,C++支持 函数重载,而过程式语言C则不支持。 函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个 函数的原型为:
void foo( int x, int y );
该 函数被C 编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。_foo_int_int这样的名字包含了 函数名、函数参数数量及类型信息,C++就是靠这种机制来实现 函数重载的。例如,在C++中, 函数 void foo( int x, int y )与void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float。
同样地, C++中的 变量除支持 局部变量外,还支持类 成员变量和 全局变量。用户所编写程序的类 成员变量可能与 全局变量同名,我们以"."来区分。而本质上, 编译器在进行编译时,与 函数的处理相似,也为类中的 变量取了一个独一无二的名字,这个名字与 用户程序中同名的 全局变量名字不同。