extern“c”

extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。让我们来详细解读这两重含义。
(1) 被 extern 限定的 函数变量extern类型的:
a. extern修饰 变量的声明。举例来说,如果文件a.c需要引用b.c中 变量int v,就可以在a.c中声明 extern int v,然后就可以 引用变量v。这里需要注意的是,被引用的 变量v的链接属性必须是外链接( external)的,也就是说a.c要引用到v,不只是取决于在a.c中声明 extern int v,还取决于 变量v本身是能够被引用到的。这涉及到 c语言的另外一个话题-- 变量作用域。能够被其他模块以extern 修饰符引用到的变量通常是 全局变量。还有很重要的一点是, extern int 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来做任何事情。就像 变量的声明一样, extern int 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语言的不同。例如,假设某个 函数的原型为:
voidfoo( 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++中的 变量除支持 局部变量外,还支持类 成员变量全局变量。用户所编写程序的类 成员变量可能与 全局变量同名,我们以"."来区分。而本质上, 编译器在进行编译时,与 函数的处理相似,也为类中的 变量取了一个独一无二的名字,这个名字与 用户程序中同名的 全局变量名字不同。

编辑本段实例

实例一

未加 extern "C"声明时的连接方式
假设在C++中,模块A的头文件如下:
// 模块A头文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
int foo( int x, int y );
#endif
在模块B中引用该 函数
// 模块B实现文件 moduleB.cpp
#i nclude "moduleA.h"
foo(2,3);
实际上,在连接阶段,连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这样的符号!
加extern "C"声明后的编译和连接方式
加extern "C"声明后,模块A的头文件变为:
// 模块A头文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
extern "C" int foo( int x, int y );
#endif
在模块B的实现文件中仍然调用foo(2,3),其结果是:
(1)模块A编译生成foo的 目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;
(2)连接器在为模块B的 目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo。
如果在模块A中 函数声明了foo为extern "C"类型,而模块B中包含的是extern int foo( int x, int y ) ,则模块B找不到模块A中的函数;反之亦然。
所以,可以用一句话概括 extern “C”这个声明的真实目的(任何语言中的任何语法特性的诞生都不是随意而为的,来源于真实世界的需求驱动。我们在思考问题时,不能只停留在这个语言是怎么做的,还要问一问它为什么要这么做,动机是什么,这样我们可以更深入地理解许多问题):
实现C++与C及其它语言的 混合编程
明白了C++中extern "C"的设立动机,我们下面来具体分析extern "C"通常的使用技巧。

extern "C"的惯用法

(1)在C++中引用C语言中的 函数变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:
extern "C"
{
#i nclude "cExample.h"
}
而在C语言的头文件中,对其外部 函数只能指定为 extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。
笔者编写的 C++引用C 函数例子工程中包含的三个文件的 源代码如下:
/* c语言头文件:cExample.h */
#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H
extern int add(int x,int y);
#endif
/* c语言实现文件:cExample.c */
#i nclude "cExample.h"
int add( int x, int y )
{
return x + y;
}
// c++实现文件,调用add:cppFile.cpp
extern "C"
{
#i nclude "cExample.h"
}
int main(int argc, char* argv[])
{
add(2,3);
return 0;
}
(注意这里如果用GCC编译的时候,请先使用gcc -c选项生成cExample.o,再使用g++ -o cppFile  cppFile.cpp cExample.o才能生成预期的 c++调用 c函数的结果,否则,使用g++ -o cppFile cppFile.cpp cExample.c 编译器会报错;而当cppFile.cpp 文件中不使用下列语句
extern "C"
{
#i nclude "cExample.h"
}
而改用
#i nclude "cExample.h"
extern "C"  int add( int x, int y );
g++ -o cppFile cppFile.cpp cExample.c的编译过程会把add 函数c++的方式解释为_foo_int_int这样的符号。
如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明 接口函数时,应加extern "C" { }。
(2)在C中引用C++语言中的 函数变量时,C++的头文件需添加extern "C",但是在C语言中不能直接引用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C" 函数声明为extern类型。
笔者编写的C引用C++ 函数例子工程中包含的三个文件的 源代码如下:
//C++头文件 cppExample.h
#ifndef CPP_EXAMPLE_H
#define CPP_EXAMPLE_H
extern "C" int add( int x, int y );
#endif
//C++实现文件 cppExample.cpp
#i nclude "cppExample.h"
int add( int x, int y )
{
return x + y;
}
/* C实现文件 cFile.c
/* 这样会编译出错:#i nclude "cppExample.h" */
extern int add( int x, int y );
int main( int argc, char* argv[] )
{
add( 2, 3 );
return 0;
}



本文转自: http://baike.baidu.com/view/2816461.htm?fromId=2814224

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值