在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern "C"


Posted in 面试题 onAugust 09, 2014
首先,作为extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。
通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数
extern “C”是连接申明(linkage declaration),被extern “C”修饰的变量和函数是按照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
#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;
}
如果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 “cExample.h” */
extern int add( int x, int y );
int main( int argc, char* argv[] )
{
add( 2, 3 );
return 0;
}

Tags in this post...

面试题 相关文章推荐
super()与this()的区别
Jan 17 面试题
包装类的功能、种类、常用方法
Jan 27 面试题
一套Java笔试题
Aug 20 面试题
怎样建立和理解非常复杂的声明?例如定义一个包含N 个指向返回 指向字符的指针的函数的指针的数组?
Mar 19 面试题
写一个在SQL Server创建表的SQL语句
Mar 10 面试题
System.Array.CopyTo()和System.Array.Clone()有什么区别
Jun 20 面试题
nohup的用法
Nov 26 面试题
主要的Ajax框架都有什么
Nov 14 面试题
设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。
Dec 30 面试题
有abstract方法的类一定要用abstract修饰吗
Mar 14 面试题
shell的种类有哪些
Apr 15 面试题
Java面试题冲刺第十五天--设计模式
Aug 07 面试题
下面关于"联合"的题目的输出是什么
Aug 06 #面试题
"引用"与指针的区别是什么
Sep 07 #面试题
经典c++面试题五
Dec 17 #面试题
经典c++面试题四
May 14 #面试题
"引用"与多态的关系
Feb 01 #面试题
将"引用"作为函数返回值类型的格式、好处和需要遵守的规则
Feb 09 #面试题
在什么时候需要使用"常引用"
Dec 31 #面试题
You might like
php miniBB中文乱码问题解决方法
2008/11/25 PHP
浏览器关闭后,能继续执行的php函数(ignore_user_abort)
2012/08/01 PHP
php使用mkdir创建多级目录入门例子
2014/05/10 PHP
PHP中Cookie的使用详解(简单易懂)
2017/04/28 PHP
关于js日期转化为毫秒数“节省20%的效率和和节省9个字符“问题
2012/03/01 Javascript
JS+CSS实现的日本门户网站经典选项卡导航效果
2015/09/27 Javascript
JQuery validate插件验证用户注册信息
2016/05/11 Javascript
JavaScript的this关键字的理解
2016/06/18 Javascript
js轮盘抽奖实例分析
2020/04/17 Javascript
jQuery焦点图轮播效果实现方法
2016/12/19 Javascript
Node.js发送HTTP客户端请求并显示响应结果的方法示例
2017/04/12 Javascript
jQuery Validate表单验证插件实现代码
2017/06/08 jQuery
解决ie img标签内存泄漏的问题
2017/10/13 Javascript
基于Vue2的独立构建与运行时构建的差别(详解)
2017/12/06 Javascript
Vue中插入HTML代码的方法
2018/09/21 Javascript
JS+canvas画布实现炫酷的旋转星空效果示例
2019/02/13 Javascript
JS使用JSON.parse(),JSON.stringify()实现对对象的深拷贝功能分析
2019/03/06 Javascript
基于js实现抽红包并分配代码实例
2019/09/19 Javascript
微信小程序 行的删除和增加操作实现详解
2019/09/29 Javascript
[01:13:51]TNC vs Serenity 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
[05:26]TI10典藏宝瓶套装外观展示
2020/07/03 DOTA
总结python爬虫抓站的实用技巧
2016/08/09 Python
Python 描述符(Descriptor)入门
2016/11/20 Python
用Python删除本地目录下某一时间点之前创建的所有文件的实例
2017/12/14 Python
无法使用pip命令安装python第三方库的原因及解决方法
2018/06/12 Python
500行python代码实现飞机大战
2020/04/24 Python
Python实现爬取并分析电商评论
2020/06/19 Python
python如何建立全零数组
2020/07/19 Python
python 实现简单的计算器(gui界面)
2020/11/11 Python
Dr. Martens马汀博士法国官网:马丁靴鼻祖
2020/01/15 全球购物
化学教师自荐信范文
2013/12/28 职场文书
班组安全员工作职责
2014/02/01 职场文书
建议书的格式
2014/05/12 职场文书
2014派出所所长群众路线对照检查材料思想汇报
2014/09/18 职场文书
镇人大副主席民主生活会对照检查材料思想汇报
2014/10/01 职场文书
2015暑期社会实践调查报告
2015/07/14 职场文书