“extern c”的使用

来源:互联网 发布:品胜淘宝旗舰店 编辑:程序博客网 时间:2024/05/12 08:39
对extern "C"的一点小认识

当我们想从C++中调用C的库时,(注,驱动是用C写的,连new、delete也不能用,郁闷)不能仅仅说明 一个外部函数,因为调用C函数的编译代码和调用C++函数的编译代码是不同的。如果你仅说明一个外部函数, C++编译器假定它是C++的函数编译成功了,但当你连接时会发现很可爱的错误 。
解决的方法就是指定它为C函数:
extern "c" 函数描述
指定一群函数的话:
extern "C"{
n个函数描述
}
如果想C和C++混用的话:
#ifdef _cplusplus
extern "C"{
#endif
n个函数描述
#ifdef _cplusplus
}
#endif

 
 

 

c与c++程序连接问题 它们之间的连接问题主要是因为c c++编绎器对函数名译码的方式不能所引起的,考虑下面两个函数
/* c*/
int strlen(char* string)
{
...
}

//c++
int strlen(char* string)
{
...
}

两个函数完全一样。在c在函数是通过函数名来识别的,而在C++中,由于存在函数的重载问题,函数的识别方式通函数名,函数的返回类型,函数参数列表三者组合来完成的。因此上面两个相同的函数,经过C,C++编绎后会产生完全不同的名字。所以,如果把一个用c编绎器编绎的目标代码和一个用C++编绎器编绎的目标代码进行连接,就会出现连接失败的错误。

解决的方法是使用extern C,避免C++编绎器按照C++的方式去编绎C函数
在头文件中定义:
extern "C" int strlen(char* string)

extern "C"
{
int strlen(char* string)
}
当C编绎器遇到extern "C"的时候就用传统的C函数编译方法对该函数进行编译。由于C编绎器不认识extern "C"这个编绎指令,而程序员又希望C,C++程序能共用这个头文件,因此通常在头文件中使用_cplusplus宏进行区分:
#if define _cplusplus
extern "C"{
#endif
int strlen(char* string)
#ifdefine _cplusplus
}
#endif
 
C++中extern“C”含义深层探索『转』 作者:宋宝华  e-mail:21cnbao@21cn.com 出处:太平洋电脑网


1.引言 


C++语言的创建初衷是“a better C”,但是这并不意味着C++中类似C语言的全局变

量和函数所采用的编译和连接方式与C语言完全相同。作为一种欲与C兼容的语言,

C++保留了一部分过程式语言的特点(被世人称为“不彻底地面向对象”),因而它

可以定义不属于任何类的全局变量和函数。但是,C++毕竟是一种面向对象的程序设

计语言,为了支持函数的重载,C++对全局函数的处理方式与C有明显的不同。 


2.从标准头文件说起 


某企业曾经给出如下的一道面试题: 


面试题 


为什么标准头文件都有类似以下的结构? 


   #ifndef __INCvxWorksh 


   #define __INCvxWorksh 


   #ifdef __cplusplus 


   extern "C" { 


   #endif 


   /*...*/ 


   #ifdef __cplusplus 


   } 


   #endif 


   #endif /* __INCvxWorksh */ 


分析 


显然,头文件中的编译宏“#ifndef __INCvxWorksh、#define __INCvxWorksh、

#endif” 的作用是防止该头文件被重复引用。

那么 


   #ifdef __cplusplus 


   extern "C" { 


   #endif 


  #ifdef __cplusplus 


   } 


   #endif 


的作用又是什么呢?我们将在下文一一道来。 


3.深层揭密extern "C" 


extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern

”的;其次,被它修饰的目标是“C”的。让我们来详细解读这两重含义。 


(1)       被extern "C"限定的函数或变量是extern类型的; 


extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字

告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。记住,下列语

句: 


extern int a; 


仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。变量a在所

有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。 


通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字

extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含

模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽

然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码

中找到此函数。 


与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用

。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “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++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编

写程序的类成员变量可能与全局变量同名,我们以"."来区分。而本质上,编译器在

进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个

名字与用户程序中同名的全局变量名字不同。

未加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 


#include "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"通常的使用技

巧。 


4.extern "C"的惯用法 


(1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h

)时,需进行下列处理:

extern "C" 





#include "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 */ 


#include "cExample.h" 


int add( int x, int y ) 





       return x + y; 





// c++实现文件,调用add:cppFile.cpp 


extern "C" 





#include "cExample.h" 





int main(int argc, char* argv[]) 





       add(2,3); 


       return 0; 





如果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 


#include "cppExample.h" 


int add( int x, int y ) 





       return x + y; 





/* C实现文件 cFile.c 


/* 这样会编译出错:#include "cExample.h" */ 


extern int add( int x, int y ); 


int main( int argc, char* argv[] ) 





       add( 2, 3 );     


       return 0; 





如果深入理解了第3节中所阐述的extern "C"在编译和连接阶段发挥的作用,就能真

正理解本节所阐述的从C++引用C函数和C引用C++函数的惯用法。对第4节给出的示例

代码,需要特别留意各个细节。


q:extern "c"是什么意思?a:用来在C++中调用C

在C++中,你写一个函数调用"func()"那么在实际生成的连接代码可能是调用

"funcXXX01"(C++会为函数名作"修饰"以区别同名的重载函数,以及类成员函数")

但如果func存在于用C编译的obj, 编译器会无法连接到正确的函数体(因为

"funcXXX01"不存在")

只有加了extern "C", C++编译器才会老实的按C格式生成调用,才能使用已有的C代码。

如果一个cpp头定义了一大串的变量;
在别的cpp文件(有好几个)也要访问这些变量,除了写一大串 extern 变量来声明外,
有没有什么省事的方法?

回复人:c2y 直接包含这个头文件就搞定了。#include <xx.h>

回复人:eastnofail 你可以把你要用的东西定义为某个类的静态成员:static int i;然后只要包含这个类定义的头文件即可

回复人:useboxer 

c2y(麻麻鱼) :
       我现在就是这样做的,但编译的时候警告,提示[LinkerWarning] Public symbol '_datasource' defined in both module E:/PROJECT/C++BUILDER/MAIN/MAIN.OBJ and E:/PROJECT/C++BUILDER/MAIN/FILTER.OBJ. 运行时datasource的值并没有在cpp之间传递

eastnofail(Char *) :
   你这个方法我有想过,但一大串变量中还有类定义和变量,并且许多变量之间并没有什么逻辑关系,觉得这个方法很勉强,想看看有没有其他更好的办法

回复人:95533 在头文件里这样写:

#ifndef EXTERN
int a;
#else
extern int a;
#endif

然后在程序里面include这个头文件。在需要用这些变量的cpp里面加一句:#define EXTERN
就可以了。

回复人:useboxer  95533:   你的方法好多了,只要写两串。但很想找一个只要写一次就行的方法

回复人:zbc 别懒,使用extern本来就是定义全局变量的标准形式,但是,你为什么一定要定义好多的全局变量呢,全局变量在存取时间及空间上是费时的

 

 

原创粉丝点击