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头文件

#ifndef __MODULE_A_H //对于模块A来说,这个宏是为了防止头文件的重复引用

#define __MODULE_A_H

int fun(int, int);

#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

#ifdef __cplusplus //而这一部分就是告诉编译器,如果定义了__cplusplus(即如果是cpp文件, extern "C"{ //因为cpp文件默认定义了该宏),则采用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这样的名字。

看下面的一个面试题:为什么标准头文件都有类似的结构?

#ifndef __INCvxWorksh /*防止该头文件被重复引用*/

#define __INCvxWorksh

#ifdef __cplusplus             //告诉编译器,这部分代码按C语言的格式进行编译,而不是C++的

extern "C"{

#endif

/*…*/

#ifdef __cplusplus

}

#endif

#endif /*end of __INCvxWorksh*/

分析:

  • 显然,头文件中编译宏"#ifndef __INCvxWorksh 、#define __INCvxWorksh、#endif"(即上面代码中的蓝色部分)的作用是为了防止该头文件被重复引用
  • 那么

#ifdef __cplusplus (其中__cplusplus是cpp中自定义的一个宏!!!)

extern "C"{

#endif

#ifdef __cplusplus

}

#endif

的作用是什么呢?

extern "C"包含双重含义,从字面上可以知道,首先,被它修饰的目标是"extern"的;其次,被它修饰的目标代码是"C"的。

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

extern是C/C++语言中表明函数和全局变量的作用范围的关键字,该关键字告诉编译器,其申明的函数和变量可以在本模块或其他模块中使用。

记住,下面的语句:

extern int a; 仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出错。

通常来说,在模块的头文件中对本模块提供给其他模块引用的函数和全局变量以关键字extern生命。例如,如果模块B要引用模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但并不会报错;它会在链接阶段从模块A编译生成的目标代码中找到该函数。

extern对应的关键字是static,static表明变量或者函数只能在本模块中使用,因此,被static修饰的变量或者函数不可能被extern C修饰。

  • 被extern "C"修饰的变量和函数是按照C语言方式进行编译和链接的:这点很重要!!!!

上面也提到过,由于C++支持函数重载,而C语言不支持,因此函数被C++编译后在符号库中的名字是与C语言不同的;C++编译后的函数需要加上参数的类型才能唯一标定重载后的函数,而加上extern "C"后,是为了向编译器指明这段代码按照C语言的方式进行编译

未加extern "C"声明时的链接方式:

//模块A头文件 moduleA.h

#idndef _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这样的符号!!!,显然这是不可能找到的,因为foo()函数被编译成了_foo的符号,因此会出现链接错误。

常见的做法可以参考下面的一个实现:

moduleA、moduleB两个模块,B调用A中的代码,其中A是用C语言实现的,而B是利用C++实现的,下面给出一种实现方法:

//moduleA头文件

#ifndef __MODULE_A_H //对于模块A来说,这个宏是为了防止头文件的重复引用

#define __MODULE_A_H

int fun(int, int);

#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

#ifdef __cplusplus //而这一部分就是告诉编译器,如果定义了__cplusplus(即如果是cpp文件, extern "C"{ //因为cpp文件默认定义了该宏),则采用C语言方式进行编译

#include"moduleA.h"

#endif

… //其他代码

#ifdef __cplusplus

}

#endif

#endif

//moduleB实现文件 moduleB.cpp //B模块的实现也没有改变,只是头文件的设计变化了

#include"moduleB.h"

int main()

{

cout<<fun(2,3)<<endl;

}

extern "C"的使用要点

1. 可以是单一语句

extern "C" double sqrt(double);

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" 等。

参考文章:

extern C使用

extern c的作用【转】_看雪..

extern C使用要点

http://blog.chinaunix.net/u/29619/showart_230148.html

http://blog.csdn.net/weiqubo/archive/2009/10/16/4681813.aspx

http://hi.baidu.com/sundl2268/blog/item/4969453d2258bae53c6d970a.html

原文来自:http://blog.csdn.net/jiqiren007/article/details/5933599

C++之 extern C的作用详解的更多相关文章

  1. extern C的作用详解

    extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码.加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C+ ...

  2. extern “C”的作用详解

    extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码.加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C+ ...

  3. Linux(centos)系统各个目录的作用详解

    Linux(centos)系统各个目录的作用详解 文件系统的类型 LINUX有四种基本文件系统类型:普通文件.目录文件.连接文件和特殊文件,可用file命令来识别. 普通文件:如文本文件.C语言元代码 ...

  4. shell脚本中常见的一些特殊符号和作用详解

    这篇文章主要介绍了shell脚本中常见的一些特殊符号和它的作用详解,总结的很简洁,容易看懂,需要的朋友可以参考下   在编写Shell脚本时,我们需要会用到各种各样的特殊符号,通过这些特殊符号可以使我 ...

  5. linux(CENTOS)系统各个目录的作用详解

    Linux(CentOS)系统各个目录的作用详解 文件的类型 LINUX有四种基本文件系统类型:普通文件.目录文件.连接文件和特殊文件,可用file命令来识别. 普通文件:如文本文件.C语言元代码.S ...

  6. MySQL中的主键,外键有什么作用详解

    MySQL中的主键,外键有什么作用详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 学关系型数据库的同学,尤其在学习主键和外键时会产生一定的困惑.那么今天我们就把这个困惑连根拔起 ...

  7. Python中__init__.py文件的作用详解

    转自http://www.jb51.net/article/92863.htm Python中__init__.py文件的作用详解 http://www.jb51.net/article/86580. ...

  8. jsp九大内置对象和其作用详解

    jsp九大内置对象和其作用详解 JSP中一共预先定义了9个这样的对象,分别为:request.response.session.application.out.pagecontext.config.p ...

  9. [转载]java中import作用详解

    [转载]java中import作用详解 来源: https://blog.csdn.net/qq_25665807/article/details/74747868 这篇博客讲的真的很清楚,这个作者很 ...

随机推荐

  1. 51nod 1624 取余最短路(set)

    题意: 佳佳有一个n*m的带权矩阵,她想从(1,1)出发走到(n,m)且只能往右往下移动,她能得到的娱乐值为所经过的位置的权的总和. 有一天,她被下了恶毒的诅咒,这个诅咒的作用是将她的娱乐值变为对p取 ...

  2. noip模拟题《迷》enc

    [问题背景]zhx 和他的妹子聊天.[问题描述]     考虑一种简单的加密算法.     假定所有句子都由小写英文字母构成, 对于每一个字母, 我们将它唯一地映射到另一个字母.例如考虑映射规则:a- ...

  3. 【bzoj3295】[Cqoi2011]动态逆序对 树套树 线段树套替罪羊树

    这个东西,关于某个数的逆序对数为在他之前比他大的在他之后比他小的数的数目的和,所以删除之前先把这个减去就好了 人傻自带超大常数,中间由于内存池开小了所以运行错误. #include<cstdio ...

  4. 【转】Oracle 查询库中所有表名、字段名、表名说明、字段名说明

    转自 :http://gis-conquer.blog.sohu.com/170243422.html 查询所有表名:select t.table_name from user_tables t; 查 ...

  5. [SCOI2016]幸运数字 线性基

    题面 题面 题解 题面意思非常明确:求树上一条链的最大异或和. 我们用倍增的思想. 将这条链分成2部分:x ---> lca , lca ---> y 分别求出这2个部分的线性基,然后合并 ...

  6. 快速傅里叶变换FFT / NTT

    目录 FFT 系数表示法 点值表示法 复数 DFT(离散傅里叶变换) 单位根的性质 FFT(快速傅里叶变换) IFFT(快速傅里叶逆变换) NTT 阶 原根 扩展知识 FFT 参考blog: 十分简明 ...

  7. 【MVVM Dev】PART_Editor的使用

    一.前言       在日常的界面开发中,我们大多使用MVVM模式进行开发.通常情况下,一个PropertyGridControl或者DataGrid的ItemsSource设置好, 然后每一列绑定好 ...

  8. 【BZOJ4767】两双手(动态规划,容斥)

    [BZOJ4767]两双手(动态规划,容斥) 题面 BZOJ 题解 发现走法只有两种,并且两维坐标都要走到对应的位置去. 显然对于每个确定的点,最多只有一种固定的跳跃次数能够到达这个点. 首先对于每个 ...

  9. 代码收藏系列--javascript--日期函数

    /** * 获取当前时间的简短函数 * @returns {String} * @@example getTimeStamp() 结果是:2017-07-12 09:21:30 */ function ...

  10. 数据压缩算法之哈夫曼编码(HUFFMAN)的实现

    HUFFMAN编码可以很有效的压缩数据,通常可以压缩20%到90%的空间(算法导论).具体的压缩率取决于数据的特性(词频).如果采取标准的语料库进行编码,一般可以得到比较满意的编码结果(对不同文件产生 ...