前言

如果向要在一个文件中使用另一个文件中的变量,不能在头文件中定义全局变量,因为被多个文件包含后会导致编译出错,并且静态的static变量,只能在本文件内使用,这时候就可以使用extern关键字。

extern 关键字:

  首先还是先看一下 extern 关键字的作用:extern关键字可以置于变量或函数前,以标示变量或函数的定义在别的文件中,提示编译器遇到此变量或函数时在其他模块中寻找其定义。

  通常情况下,比如我们在头文件  "b.h"  中声明了一个函数,然后在 "b.cpp" 中实现了该函数,当在 "main.cpp" 中调用 "b.h" 中声明的函数时,只需要在 ""main.cpp" 中 #include "b.h" 就可以了。例子如下:

  1. //b.h
  2. #ifndef _B_
  3. #define _B_
  4.  
  5. #include<iostream>
  6. using namespace std;
  7.  
  8. void test();
  9.  
  10. #endif // _B_
  1. #include "b.h"
  2. void test()
  3. {
  4. cout << "test" << endl;
  5. }
  1. #include<iostream>
  2. #include"b.h"
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. test();
  9. system("pause");
  10. return ;
  11. }

除了通过 #include "b.h" 这样的方式能调用到 "b.h" 中的函数外,还有下面这种方式也能调用到"b.h" 的函数。

  1. #include<iostream>
  2. //#include"b.h" //在这里,不注释掉也是可以得,但是在对于 变量 来说时就必须注释掉了
  3.  
  4. using namespace std;
  5.  
  6. extern void test(); //告诉编译器test()函数声明在其他文件中
  7.  
  8. int main()
  9. {
  10. test();
  11. system("pause");
  12. return ;
  13. }

上面是对于函数而言,那么要是在 "b.h" 中定义了一个全局变量 int x,(记住是全局变量哦!)现在我们想在 "main.cpp" 中访问变量 x, 那该怎么办呢? 会不会 #include "b.h" 后就可以直接访问了呢?我们先试一下:

  1. //b.h
  2. #ifndef _B_
  3. #define _B_
  4.  
  5. #include<iostream>
  6. using namespace std;
  7.  
  8. int x = ;
  9.  
  10. void test();
  11.  
  12. #endif // _B_
  1. //main.cpp
  2. #include "b.h"
  3.  
  4. int main()
  5. {
  6. cout << "x=" << x << endl;
  7. system("pause");
  8. return ;
  9. }

我们在 "main.cpp" 中输出变量 x, 在vs2017中会报错。  (上一个增量链接没有生成它;正在执行完全链接,error LNK2005: "int x" (?x@@3HA) 已经在 b.obj 中定义)

通过 #include "b.h" 这种方式就想访问到变量 x 太天真了,是访问不到的,因为 "b.h" 的全局变量 x 的访问作用域是文件作用域,它只能在 "b.h" 这个文件中进访问,记住是只能在 "b.h" 中进行访问,在 "b.cpp"中通过 #include "b.h" 你也是不能访问的。 那么现在我们有没有其他方式能在 "main.cpp" 中访问到变量 x 呢?当然有,通过 "extern" 关键字能达到目的。用法如下:

  1. //main.cpp
  2. //#include "b.h" //一定要注释掉
  3. #include <iostream>
  4. using namespace std;
  5. extern int x;
  6.  
  7. int main()
  8. {
  9. cout << "x=" << x << endl;
  10. system("pause");
  11. return ;
  12. }

说明:如果在一个文件中使用extren引入外部变量,在这个文件中修改这个变量,就等于修改了该外部变量。

说了那么多废话,终于把 extern 关键字说清楚了。接下来这个才是extern "C" 。

extern "C"

  要说清楚 extern "C" 是怎么一回事,还得先说说C++函数重载的那些事。C++有函数重载,而C语言是没有函数重载的。函数重载是指两个或多个函数之间函数名相同,参数列表不同,参数列表不同可以是参数的个数不同,或者是参数的个数相同但参数类型不同,需要注意的是如果函数名相同,参数列表完全相同但返回值类型不同是不能构成函数重载的。C++有函数重载是因为当生成obj中间文件/目标文件的时候,C++编译器把原函数名与参数信息结合,产生了一个独特的内部名字,比如有两个函数 void foo(int x) 和 void foo(void) ,最终产生的内部名字就是 _foo_int 和 _foo_void (实际产生的内部名字的命名规则应该不是这样的,这里我们并不关心它的命名规则是怎样的,只需要体会这个意思就可以了),通过这样编译器就能区分 void foo(int x) 和 void foo(void)这两个函数了。但是在C语言里面,并不会产生这样的内部名字,如果C语言里面有两个函数 void foo(int x) 和void foo(void),那么当生成obj中间文件/目标文件的时候,产生的名字就是 _foo 和 _foo 这样两个名字相同,C编译器就不能将两个函数区分开了,所以C语言里面也就没了函数重载。

  正是由于C++编译器 和 C编译器对函数名处理方式的不同,当我们的 C++ 程序调用 C 程序或者 C 程序调用 C++程序时就会存在问题。 有了问题那当然就得解决,于是就有了 extern "C" 的出现。

  所以说到底 extern "C" 的作用是用来解决名字匹配问题,实现 C 与 C++ 的混合编程。摆这么一句话在这里显得很苍白无力,还是举例说明一下。

C++中函数重载,编译器的命名规则是

  1. int fun(int a, int b)
  2. {
  3. return a + b;
  4. }
  5. char fun(char a, char b)
  6. {
  7. return a + b;
  8. }
  9.  
  10. void main()
  11. {
  12. cout<<fun('A','B')<<endl;
  13. cout<<fun(, )<<endl;
  14. }

上述C++的两个重载函数被编译器命名为下:

在不同的平台的编译器中,命名规则不一定相同,其中的DDD和HHH代表返回值、第一个参数类型、第二个参数类型
如果再加一个参数
  1. int fun(int a, int b, char c)
  2. {
  3. return a + b;
  4. }

命名会变为

可以得出,C++编译器的函数命名规则

而在C语言中,编译器将函数命名为
所以不论多少个函数,编译器都会命名为一样的函数,即使你的参数不一样,编译器也无法调动这个函数,所以就会报错
在C++中加extern “C” 
  1. extern "C" int fun(int a, int b)
  2. {
  3. return a + b;
  4. }
  5. extern "C" char fun(char a, char b)
  6. {
  7. return a + b;
  8. }
  9.  
  10. void main()
  11. {
  12. cout<<fun('A','B')<<endl;
  13. cout<<fun(, )<<endl;
  14. }

上述C++程序中,加一个extern "C"没问题,加两个就会报错了,加了extern "C"就等于用C语言的方式编译,函数的命名规则会变成C语言的,然而C语言不允许函数的重载。

在C++程序中调用被C编译器编译后的函数,加extern“C“声明,为了预防函数的重载出现。

函数重载的另一个问题

为什么返回值不能确定函数重载,如果有以下程序
  1. int fun(int a, int b)
  2. {
  3. return a + b;
  4. }
  5. char fun(int a, int b)
  6. {
  7. return a + b;
  8. }

如果按照C++编译器的命名,这两个函数的命名也是不同的,为什么不能重载呢?

这时候并不是不行,而是调用一个函数,可以调用一个,也可以调用另一个,然后编译器并不知道要调到哪个,出现了二义性,而不同的函数参数,在编译时就决定了调用哪个,编译器也不知道要将返回值给哪个类型,所以仅靠返回值是不能用来重载函数的。

[参考:https://www.cnblogs.com/418ks/p/6837669.html]

extern "C" 与函数重载的更多相关文章

  1. C++函数重载实现的原理以及为什么在C++中使用用C语言编译的函数时,要在函数名称前面加上extern "C"声明

    C++相对于C语言而言支持函数重载是其极大的一个特点,相信在使用C语言的时候大家如果要写一个实现两个整型数据相加的函数还要写一个浮点型数据相加的函数,那么这两个函数的名字绝对不可以一样,这样无疑在我们 ...

  2. 函数定义从零开始学C++之从C到C++(一):const与#define、结构体对齐、函数重载name mangling、new/delete 等

    今天一直在学习函数定义之类的问题,下午正好有机会和大家共享一下. 一.bool 类型 逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC+ ...

  3. const和非const函数重载

    成员函数后面加const,表示在该函数中不能对类的数据成员进行改变,比如下面的代码: #include <stdio.h> class A { private: mutable int a ...

  4. 4.C++中的函数重载,C++调用C代码,new/delete关键字,namespace(命名空间)

    本章主要内容: 1)函数重载 2)C++调用C代码 3)new/delete关键字实现动态内存分配 4)namespace命名空间 大家都知道,在生活中,动词和不同的名词搭配一起,意义都会大有不同,比 ...

  5. C++命名空间、函数重载、缺省参数、内联函数、引用

    一 .C++入门 1.C++关键字 2.命名空间 3.C++输入&输出 4.缺省参数 5.函数重载 6.引用 7.内联函数 8.auto关键字 9.基于范围的for循环 10.指针空值null ...

  6. const与#define、结构体对齐、函数重载name mangling、new/delete 等

    一.bool 类型 逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC++中为1个字节. 声明方式:bool result; result ...

  7. C++ 函数重载和参数的缺省值

    一.函数重载 1.1 重载的起源 自然语言中,一个词可以有许多不同的含义,即该词被重载了.人们可以通过上下文来判断该词到底是哪种含义."词的重载"可以使语言更加简练.例如" ...

  8. 【C++初学者自学笔记二】函数重载(模块一)

    1.概念:同意作用域的一组参数列表不同,函数名相同的函数,这组函数叫函数重载(C语言是不能定义相同名称的函数,但是C++可以允许定义). 2作用:重载函数通常来命名一组功能相似的函数,这样做减少了函数 ...

  9. c++中的函数重载、函数重写、函数重定义

    目录 一.函数重载 二.函数重写 三.函数重定义 为了更加深刻的理解 函数重载.重写.重定义,我们可以带着如下这两个问题去思考: 1.子类中是否可以定义父类中的同名成员?为什么? 可以,因为子类与父类 ...

随机推荐

  1. Win10安装2 —— 版本的选择与下载

    本文内容皆为作者原创,如需转载,请注明出处:https://www.cnblogs.com/xuexianqi/p/12368795.html 一:各个版本的区别 1.Windows10 Home(家 ...

  2. UVa - 12050 Palindrome Numbers (二分)

    Solve the equation: p ∗ e −x + q ∗ sin(x) + r ∗ cos(x) + s ∗ tan(x) + t ∗ x 2 + u = 0 where 0 ≤ x ≤ ...

  3. windows-problem :电脑上网的无线图标带有黄色星号,但不影响正常上网!

    电脑可以上网,但是无线图标带有黄色星号如何解决? 进入“网络和共享中心”,点击“更改适配器设置”,看看是不是有两个以上的“本地连接”,只有一个有用,其他的删除即可.

  4. ROS2GO 与WIN10 双系统安装

    关于ROS2GO的一些心得: 我是一个ROS的探索者,在接触ROS一段时间后,意外发现了一个关于ROS2GO的信息,是天之博特的微信公众号发表的.简单来说ROS2GO就是一个装了ROS的Ubuntu系 ...

  5. RN开发-组件View,Text

    1.外联js文件(RN自定义组件)        module.exports=Header; 导出模块         const Header=require('./header'); 导入外部j ...

  6. 【NOIP2011提高组】计算系数

    计算系数 算法:真·滚动数组模拟!!! 马上CSP/S了,这是远在今年暑假前的一天的校内考试题中的一道.当时做的时候不会组合数,不会二项式定理,不会DP,不会……只知道应该n*n的空间存一个杨辉三角形 ...

  7. AntDesign(React)学习-7 Menu添加事件

    今天花了大半天时间从老家回到工作地,路上因为肺炎封堵挺厉害,希望国家挺过这个难关,要不大家都失业可就惨了,上一篇做了一个展示数据的demo,这一篇研究antd Menu item点击事件 1.还是先看 ...

  8. 神器扒网站——teleport ultra

    在平时的开发或者学习的过程中,我们难免会看到一些让人心动的网站,于是自己想把它搞下来,自己手工一个文件一个文件把它保存下来也可以实现,完成了也累得够呛,有一款扒站的神器,可以把你所喜欢的目标网站整个网 ...

  9. openstack入门及应用

    一.OpenStack云计算的介绍 (一)云计算的服务类型 IAAS:基础设施即服务,如:云主机 PAAS:平台即服务,如:docker SAAS:软件即服务,如:购买企业邮箱,CDN 传统IT IA ...

  10. HTML的学习结构

    HTML的学习结构 HTML的背景 HTML的创建 HTML的网页基本结构 HTML的基本标签 HTML的图像标签 HTML的链接标签 HTML的列表标签和表格标签 HTML的媒体元素(视频+音频) ...