extern C的作用具体解释

转载于:http://www.cnblogs.com/rollenholt/archive/2012/03/20/2409046.html

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”的。让我们来具体解读这两重含义。

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

  extern是C/C++语言中表明函数和全局变量作用范围(可见性)的keyword,该keyword告诉编译器,其声明的函数和变量能够在本模块或其他模块中使用。记住,下列语句:

  extern int a;

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

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

  通常,在模块的头文件里对本模块提供给其他模块引用的函数和全局变量以keywordextern声明。比如,假设模块B欲引用该模块A中定义的全局变量和函数时仅仅需包括模块A的头文件就可以。

这样。模块B中调用模块A中的函数时,在编译阶段。模块B尽管找不到该函数,可是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。

  与extern相应的keyword是static。被它修饰的全局变量和函数仅仅能在本模块中使用。因此,一个函数或变量仅仅可能被本模块使用时,其不可能被extern “C”修饰。

  被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函数样例project中包括的三个文件的源码例如以下:

/* c语言头文件:cExample.h */

#ifndef C_EXAMPLE_H

#define C_EXAMPLE_H

extern int add(int x,int y);     //注:写成extern "C" int add(int , int ); 也能够

#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"        //注:此处不妥,假设这样编译通只是,换成 extern "C" int add(int , int ); 能够通过

}

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++函数样例project中包括的三个文件的源码例如以下:

//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节给出的演示样例代码,须要特别留意各个细节。

时间: 2024-10-12 02:42:05

extern C的作用具体解释的相关文章

C++编译时函数名修饰约定规则(很具体),MFC提供的宏,extern "C"的作用

调用约定: __cdecl __fastcall与 __stdcall,三者都是调用约定(Calling convention),它决定以下内容:1)函数参数的压栈顺序,2)由调用者还是被调用者把参数弹出栈,3)以及产生函数修饰名的方法. 1.__stdcall调用约定:函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈, 2._cdecl是C和C++程序的缺省调用方式.每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大.函数采用

C#中static void Main(string[ ] args)中的作用及解释

C#中static void Main(string[ ] args)中的作用及解释 static 表示方法是静态的就是说方法在程序被编译的时候就被分配了内存,使用的时候不用生成某个类型的对象,知道程序退出才释放. void 表示方法没有返回值,就是方法没有renturn 关键字. main 是方法名,当然这个方法是特殊的方法,是Main()就是说是主函数,就是整个程序的入口,程序被编译后从这里开始执行,这也是main方法要是static的原因,因为在啥都没做的时候这个函数就得存在. strin

extern “C”的作用详解

extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码.加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的.由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名:而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名. 这个功能十分有用处,因为在C++出现以前,很多代码都是C语言写的,而且很底层的库也是C语言写的,为了

第2例——extern关键字的作用

1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说extern有两个作用,第一个,当它与"C"一起连用时,如: extern "C" void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目全非,可能是[

extern “C”的作用

1.引言 C++语言的创建初衷是“a better C”,但是这并不意味着C++中类似C语言的全局变量和函数所采用的编译和连接方式与C语言完全相同.作为一种欲与C兼容的语言,C++保留了一部分过程 式语言的特点(被世人称为“不彻底地面向对象”),因而它可以定义不属于任何类的全局变量和函数.但是,C++毕竟是一种面向对象的程序设计语言,为了支 持函数的重载,C++对全局函数的处理方式与C有明显的不同. 2.从标准头文件说起 某企业曾经给出如下的一道面试题: 面试题 为什么标准头文件都有类似以下的结

extern "c"的作用详解

extern "c"的主要作用就是为了能够正确实现C++代码调用其它C语言代码.加上extern "c"后,会指示编译器这部分代码按照C语言的风格进行编译.         由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名:C语言并不支持函数重载,因此编译C代码的函数时不会带上函数的参数类型,一般只包括函数名. 这个功能十分有用,因为在C++出现以前,很多代码都是C语言写的,而且底层的库也是C语言写的,为了更

extern "c" 的作用

作用:实现C和C++混合编程. 原理:C和C++编译器编译之后,函数名会编译成不同的名字,链接阶段名字查找会找不到目标,后面实例中会详解. 用法:①.c文件中定义的函数,.cpp文件要调用时,该.cpp文件中要用extern "C"声明该函数:②反过来,.cpp文件中定义的函数,.c文件要调用,则同样要在.cpp文件中用extern "C"声明,.c文件只用普通的extern声明. 两个源文件分别编译成.o文件,再链接成执行文件.链接.o文件生成执行文件时,必须得用

extern关键词的作用

extern关键词有两个用途: 1. extern "C“ 表示修饰的函数是在C中定义的,告诉编译器编译时按照C的规则来编译.这是由于C++为了实现函数的重载,在编译时会将函数翻译为函数名加参数的形式,例如void foo( int x, int y );会被翻译成类似_foo_int_int的样子,不同的编译器格式不同,但原理类似.那么在C++中调用C的函数时,会找不到C中的函数,因为在C中该函数被翻译成了_foo之类的形式.为了能够在C++中兼容C,所以在include头文件时,需要加上ex

Android资源文件中各种XML的作用与解释

众所周知,XML是一种可扩展标记语言,它被用来传输和存储数据.在Android中也会随处可见XML文件,包括一个android项目不可缺少的AndroidManifest.xml清单文件,res资源文件目录下的anim/drawable/layout/menu/values中等,目录截图如下.其中清单文件中内容最多最复杂,完全可以在其他文章中再来讲解,所以本文主要讲解res目录下的XML的作用与内容. 一.anim目录 anim目录下的xml主要是用于android中的动画,包括Frame an