前言

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

extern 关键字:

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

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

 //b.h
#ifndef _B_
#define _B_ #include<iostream>
using namespace std; void test(); #endif // _B_
 #include "b.h"
void test()
{
cout << "test" << endl;
}
 #include<iostream>
#include"b.h" using namespace std; int main()
{
test();
system("pause");
return ;
}

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

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

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

 //b.h
#ifndef _B_
#define _B_ #include<iostream>
using namespace std; int x = ; void test(); #endif // _B_
 //main.cpp
#include "b.h" int main()
{
cout << "x=" << x << endl;
system("pause");
return ;
}

我们在 "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" 关键字能达到目的。用法如下:

 //main.cpp
//#include "b.h" //一定要注释掉
#include <iostream>
using namespace std;
extern int x; int main()
{
cout << "x=" << x << endl;
system("pause");
return ;
}

说明:如果在一个文件中使用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++中函数重载,编译器的命名规则是

 int fun(int a, int b)
{
return a + b;
}
char fun(char a, char b)
{
return a + b;
} void main()
{
cout<<fun('A','B')<<endl;
cout<<fun(, )<<endl;
}

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

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

命名会变为

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

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

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

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

函数重载的另一个问题

为什么返回值不能确定函数重载,如果有以下程序
 int fun(int a, int b)
{
return a + b;
}
char fun(int a, int b)
{
return a + b;
}

如果按照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. Vue.js_devtools_5.1.0.zip【需要的可自行下载】

    点击链接下载: Vue.js_devtools_5.1.0.zip

  2. php文件操作(最后进行文件常用函数封装)

    文件信息相关API $filename="./1-file.php"; //filetype($filename):获取文件的类型,返回的是文件的类型 echo '文件类型为:', ...

  3. js秒数倒计时

    代码 /** * 调用回调函数 * @param callback 回调函数体 * @param args 参数 */ execCallback: function (callback, args) ...

  4. 【你不知道的javaScript 上卷 笔记4】javaScript 中闭包的一些运用

    什么是闭包 闭包是javaScript语言的一种特性,在 javaScript 中以函数作为承接单元.当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行. fun ...

  5. [CF1216C] White Sheet - 离散化,模拟

    虽然分类讨论应该是比较推崇的解法,但是我就是喜欢暴力 #include <bits/stdc++.h> using namespace std; #define int long long ...

  6. PP: Shape and time distortion loss for training deep time series forecasting models

    Problem: time series forecasting Challenge: forecasting for non-stationary signals and multiple futu ...

  7. Selenium3+python自动化013-自动化数据驱动及模型介绍

    一.查看当前运行的浏览等相关信息 driver=webdriver.Chrome() print(driver.capabilities["version"]) #浏览器版本 pr ...

  8. 全栈高级web前端工程师的必经之路

    这是最近整理的一个进阶高级web工程师的技术栈 同时也是激励自己每天来看一下离真正的王者还有多少距离! 过段时间再来反思一下自己进步了多少? ------------------------20190 ...

  9. lnmp1.5一键安装包安装lnmpa后,添加站点

    lnmp1.5一键安装包安装lnmpa后,添加站点 (1)添加站点 (2)配置apache配置文件 在/usr/local/apache/conf/vhost文件夹下,修改webApp站点配置文件ap ...

  10. 2019-08-07 纪中NOIP模拟B组

    T1 [JZOJ1385] 直角三角形 题目描述 二维平面坐标系中有N个位置不同的点. 从N个点选择3个点,问有多少选法使得这3个点形成直角三角形. 数据范围 $3 \leq N \leq 1500$ ...