C和C++对函数的处理方式是不同的。extern "C"是使C++能够调用C写作的库文件的一个手段,如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern "C"来说明。
 

时常在cpp的代码之中看到这样的代码:

#ifdef __cplusplus
extern "C" {
#endif
//一段代码
#ifdef __cplusplus
}
#endif

这样的代码到底是什么意思呢?首先,__cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码,也就是说,上面的代码的含义是:如果这是一段cpp的代码,那么加入extern "C"{和}处理其中的代码。

要明白为何使用extern "C",还得从cpp中对函数的重载处理开始说起。在c++中,为了支持重载机制,在编译生成的汇编码中,要对函数的名字进行一些处理,加入比如函数的返回类型等等。而在C中,只是简单的函数名字而已,不会加入其他的信息.也就是说:C++和C对产生的函数名字的处理是不一样的。

比如下面的一段简单的函数,我们看看加入和不加入extern "C"产生的汇编代码都有哪些变化:

int f(void)
{
return 1;

}

在加入extern "C"的时候产生的汇编代码是:

.file "test.cxx"
.text
.align 2
.globl _f
.def _f; .scl 2; .type 32; .endef
_f:
pushl %ebp
movl %esp, %ebp
movl $1, %eax
popl %ebp
ret

但是不加入了extern "C"之后:

.file "test.cxx"
.text
.align 2
.globl __Z1fv
.def __Z1fv; .scl 2; .type 32; .endef
__Z1fv:
pushl %ebp
movl %esp, %ebp
movl $1, %eax
popl %ebp
ret

两段汇编代码同样都是使用gcc -S命令产生的,所有的地方都是一样的,唯独是产生的函数名,一个是_f,一个是__Z1fv。

明白了加入与不加入extern "C"之后对函数名称产生的影响,我们继续我们的讨论:为什么需要使用extern "C"呢?C++之父在设计C++之时,考虑到当时已经存在了大量的C代码,为了支持原来的C代码和已经写好C库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。

试想这样的情况:一个库文件已经用C写好了而且运行得很良好,这个时候我们需要使用这个库文件,但是我们需要使用C++来写这个新的代码。如果这个代码使用的是C++的方式链接这个C库文件的话,那么就会出现链接错误.我们来看一段代码:首先,我们使用C的处理方式来写一个函数,也就是说假设这个函数当时是用C写成的:

//f1.c
extern "C"
{
void f1()
{
return;
}
}

编译命令是:gcc -c f1.c -o f1.o 产生了一个叫f1.o的库文件。再写一段代码调用这个f1函数:

// test.cxx
//这个extern表示f1函数在别的地方定义,这样可以通过
//编译,但是链接的时候还是需要
//链接上原来的库文件.
extern void f1();
int main()
{
f1();
return 0;
}

通过gcc -c test.cxx -o test.o 产生一个叫test.o的文件。然后,我们使用gcc test.o f1.o来链接两个文件,可是出错了,错误的提示是:

test.o(.text + 0x1f):test.cxx: undefine reference to 'f1()'

也就是说,在编译test.cxx的时候编译器是使用C++的方式来处理f1()函数的,但是实际上链接的库文件却是用C的方式来处理函数的,所以就会出现链接过不去的错误:因为链接器找不到函数。

因此,为了在C++代码中调用用C写成的库文件,就需要用extern "C"来告诉编译器:这是一个用C写成的库文件,请用C的方式来链接它们。

比如,现在我们有了一个C库文件,它的头文件是f.h,产生的lib文件是f.lib,那么我们如果要在C++中使用这个库文件,我们需要这样写:

extern "C"
{
#include "f.h"
}

回到上面的问题,如果要改正链接错误,我们需要这样子改写test.cxx:

extern "C"
{
extern void f1();
}
int main()
{
f1();
return 0;
}

重新编译并且链接就可以过去了。

总结

C和C++对函数的处理方式是不同的.extern "C"是使C++能够调用C写作的库文件的一个手段,如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern "C"来说明。

探索C++的秘密之详解extern的更多相关文章

  1. 探索C++的秘密之详解extern "C",这就是为什么很多.lib被我们正确调用确总是无法解析的。

    (转载,绝对的有用) lib被我们正确调用确总是无法解析.这是C++编译和C编译的区别 时常在cpp的代码之中看到这样的代码: #ifdef __cplusplus extern "C&qu ...

  2. 【转】探索C++的秘密之详解extern

    本文转自:http://i.cnblogs.com/EditPosts.aspx?opt=1 时常在cpp的代码之中看到这样的代码: #ifdef __cplusplus extern "C ...

  3. (转)探索C++的秘密之详解extern "C",这就是为什么很多.lib被我们正确调用确总是无法解析的。

    (转载,绝对的有用) lib被我们正确调用确总是无法解析.这是C++编译和C编译的区别 时常在cpp的代码之中看到这样的代码: #ifdef __cplusplus extern "C&qu ...

  4. 探索c#之一致性Hash详解

    阅读目录: 使用场景 算法原理 虚拟节点 代码示例 使用场景 以Redis为例,当系统需要缓存的内容超过单机内存大小时,例如要缓存100G数据,单机内存仅有16G时.这时候就需要考虑进行缓存数据分片, ...

  5. Webpack探索【5】--- plugins详解

    本文主要讲plugins相关内容. https://gitbook.cn/gitchat/column/59e065f64f7fbe555e479204/topic/59e96d87a35cf44e1 ...

  6. Webpack探索【3】--- loader详解

    本文主要说明Webpack的loader相关内容.

  7. Webpack探索【12】--- externals详解

    本文主要讲externals相关内容. https://segmentfault.com/a/1190000012113011

  8. Mysql探索之Explain执行计划详解

    前言 如何写出效率高的SQL语句,提到这必然离不开Explain执行计划的分析,至于什么是执行计划,如何写出高效率的SQL,本篇文章将会一一介绍. 执行计划 执行计划是数据库根据 SQL 语句和相关表 ...

  9. Dom探索之基础详解

    认识DOM DOM级别 注::DOM 0级标准实际并不存在,只是历史坐标系的一个参照点而已,具体的说,它指IE4.0和Netscape Navigator4.0最初支持的DHTML. 节点类型 注:1 ...

随机推荐

  1. CDN技术之-介绍

    “第一公里”是指万维网流量向用户传送的第一个出口,是网站服务器接入互联网的链路所能提供的带宽.这个带宽决定了一个网站能为用户提供的访问速度和并发访问量.如果业务繁忙,用户的访问数越多,拥塞越严重,网站 ...

  2. 提供 web前端、H5、html页面 技术服务

    如有前端页面的需求请在评论区留言  第一时间进行回复

  3. 【Windows、SVN】在Windows服务器下安装SVN,并在客户端能维护代码版本

    1.分别在客户端和服务器端安装软件 在网上搜索一下安装包的下载地址(这里暂不介绍) 得到2个安装文件 Server是装在服务器端的,另外一个装在客户端 2.安装SVN服务器端 基本一致下一步即可 特殊 ...

  4. dotnet core项目的nuget存储路径

    Where's the package location in aspnet core For project.json the nuget directory is in the user prof ...

  5. java.lang.String中的replace方法到底替换了一个还是全部替换了。

    你没有看错我说的就是那个最常用的java.lang.String,String可以说在Java中使用量最广泛的类了. 但是我却发现我弄错了他的一个API(也可以说是两个API),这个API是关于字符串 ...

  6. 92、R语言分析案例

    1.读取数据 > bank=read.table("bank-full.csv",header=TRUE,sep=";") > 2.查看数据结构 & ...

  7. spss乱码问题解决

    spss乱码问题解决 方法1:网友kuangsir6提供 选择字体为:DFKai-SB 格式(我并没有找到这个格式) 方法是 SPSS(PASW)---Edit---Options---Viewer- ...

  8. python - 小米推送使用

    1. 小米文档及SDK下载 1.文档介绍 https://dev.mi.com/console/doc/detail?pId=863 sdk说明: 2.开发者需要登录开发者网站(申请AppID, Ap ...

  9. Java中深度克隆和浅度克隆

    一:使用目的: 就是为了快速构造一个和已有对象相同的副本.如果需要克隆对象,一般需要先创建一个对象,然后将原对象中的数据导入到新创建的对象中去,而不用根据已有对象进行手动赋值操作. 二:Object中 ...

  10. OpenSuSe开启sshd服务

    需要测试OpenSuSE11 x64上mysql性能,发现很多东西与centos以及红帽有差别.其中最切身的就是sshd服务的开启. 安装好OpenSuSE 11后,发现ssh连接不上去,可以ping ...