from:https://blog.csdn.net/z702143700/article/details/46805241

一、 extern做变量声明

l  声明extern关键字的全局变量和函数可以使得它们能够跨文件被访问。

我们一般把所有的全局变量和全局函数的实现都放在一个*.cpp文件里面,然后用一个同名的*.h文件包含所有的函数和变量的声明。如:

[cpp] view plaincopy
  1. /*Demo.h*/
  2. #pragma  once
  3. extern int a;
  4. extern int b;
  5. int add(int a,int b);
[cpp] view plaincopy
  1. /*Demo.cpp*/
  2. #include "Demo.h" /*这句话写或者不写在本例中都行,不过建议不写*/
  3. /*不写不会出问题,写了有些情况下会出问题,下面有解释*/
  4. int a =10;
  5. int b =20;
  6. int add(intl,intr)
  7. {
  8. return l +r;
  9. }

如果将Demo.cpp写成了Demo.c,编译器会告诉你说无法解析的外部符号。

因为Demo.c里面的实现会被C编译器处理,然而C++和C编译器在编译函数时存在差异,所以会存在找不到函数的情况。

l  全局函数的声明语句中,关键字extern可以省略,因为全局函数默认是extern类型的。

l 声明和定义

extern int a; //属于声明  extern int a = 10; //属于定义,同下

extern char g_str[]="123456";//这个时候相当于没有extern

如果在一个文件里定义了char g_str[] = "123456";在另外一个文件中必须使用extern char g_str[ ];来声明。不能使用extern char* g_str;来声明。extern是严格的声明。且extern char* g_str只是声明的一个全局字符指针。

注:声明可以拷贝n次,但是定义只能定义一次。

二、extern “C”

l  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,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。

实现C++与C及其它语言的混合编程:

参考:https://blog.csdn.net/gobitan/article/details/1532769

被extern"C"修饰的变量和函数是按照C语言方式编译和连接的,未加extern “C”则按照声明时的编译方式。

l  extern "C"的惯用法

(1)“C++使用C”在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:

extern "C"

{

#include "cExample.h" //C++中使用C的函数和变量

}

而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern"C"声明,在.c文件中包含了extern"C"时会出现编译语法错误。

(2)“C使用C++”在C中引用C++语言中的函数和变量时,C++的头文件需添加extern"C",但是在C语言中不能直接引用声明了extern"C"的该头文件,应该仅将C文件中将C++中定义的extern"C"函数声明为extern类型。

三、 extern 和static

(1)extern表明该变量在别的地方已经定义过了,在这里要使用那个变量。

(2)static 表示静态的变量,分配内存的时候,存储在静态区,不存储在栈上面。

static作用范围是内部连接的关系这和extern有点相反。它和对象本身是分开存储的,extern也是分开存储的,但是extern可以被其他的对象用extern引用,而static不可以,只允许对象本身用它。具体差别首先,static与extern是一对“水火不容”的家伙,也就是说extern和static不能同时修饰一个变量;其次,static修饰的全局变量声明与定义同时进行,也就是说当你在头文件中使用static声明了全局变量后,它也同时被定义了;最后,static修饰全局变量的作用域只能是本身的编译单元,也就是说它的“全局”只对本编译单元有效,其他编译单元则看不到它,如:

[cpp] view plaincopy
  1. /*test1.h*/
  2. #ifndef TEST1H
  3. #define TEST1H
  4. static char g_str[]="123456";
  5. void fun1();
  6. #endif
[cpp] view plaincopy
  1. /*test1.cpp*/
  2. #include "test1.h"
  3. void fun1()
  4. {
  5. cout <<g_str<<endl;
  6. }
[html] view plaincopy
  1. /*test2.cpp*/
  2. #include "test1.h"
  3. void fun2()
  4. {
  5. cout <<g_str<<endl;
  6. }

以上两个编译单元可以连接成功,当你打开test1.obj时,你可以在它里面找到字符串"123456",同时你也可以在test2.obj中找到它们,它们之所以可以连接成功而没有报重复定义的错误是因为虽然它们有相同的内容,但是存储的物理地址并不一样,就像是两个不同变量赋了相同的值一样,而这两个变量分别作用于它们各自的编译单元。也许你比较较真,自己偷偷的跟踪调试上面的代码,结果你发现两个编译单元(test1,test2)的g_str的内存地址相同,于是你下结论static修饰的变量也可以作用于其他模块,但是我要告诉你,那是你的编译器在欺骗你,大多数编译器都对代码都有优化功能,以达到生成的目标程序更节省内存,执行效率更高,当编译器在连接各个编译单元的时候,它会把相同内容的内存只拷贝一份,比如上面的"123456",位于两个编译单元中的变量都是同样的内容,那么在连接的时候它在内存中就只会存在一份了,如果你把上面的代码改成下面的样子,你马上就可以拆穿编译器的谎言:

[html] view plaincopy
  1. /*test1.cpp*/
  2. #include "test1.h"
  3. void fun1()
  4. {
  5. g_str[0]=''a'';
  6. cout <<g_str<<endl;
  7. }
[html] view plaincopy
  1. /*test2.cpp*/
  2. #include "test1.h"
  3. void fun2()
  4. {
  5. cout <<g_str<<endl;
  6. }
[html] view plaincopy
  1. /*main.cpp*/
  2. void main()
  3. {
  4. fun1();// a23456
  5. fun2();// 123456
  6. }

这个时候你在跟踪代码时,就会发现两个编译单元中的g_str地址并不相同,因为你在一处修改了它,所以编译器被强行的恢复内存的原貌,在内存中存在了两份拷贝给两个模块中的变量使用。正是因为static有以上的特性,所以一般定义static全局变量时,都把它放在原文件中而不是头文件,这样就不会给其他模块造成不必要的信息污染,同样记住这个原则吧!

四、extern和const

C++中const修饰的全局常量具有跟static相同的特性,即它们只能作用于本编译模块中,且static修饰的是全局变量,但是const可以与extern连用来声明该常量可以作用于其他编译模块中,如extern const char g_str[];

然后在原文件中别忘了定义:const char g_str[] = "123456";

所以当const单独使用时它就与static相同,而当与extern一起合作的时候,它的特性就跟extern的一样了!所以对const我没有什么可以过多的描述,我只是想提醒你,

const char* g_str = "123456" 与 const char g_str[] ="123465"是不同的,前面那个const修饰的是char *而不是g_str,它的g_str并不是常量,它被看做是一个定义了的全局变量(可以被其他编译单元使用), 所以如果你像让char* g_str遵守const的全局常量的规则,最好这么定义const char* const g_str="123456"。

深入理解extern用法相关推荐

  1. 一文彻底搞懂extern用法

    一.定义和声明的区别 声明:用来告诉编译器变量的名称和类型,而不分配内存,不赋初值. 定义:为了给变量分配内存,可以为变量赋初值. 注:定义要为变量分配内存空间:而声明不需要为变量分配内存空间. 二. ...

  2. c语言中extern变量,C语言中的Extern用法

    C语言中的Extern用法 (2010-07-28 12:50:39) 标签: 杂谈 分类: 技术 C语言中的Extern用法 网上有很多帖子问C语言中Extern的用法,而且回答的详细程度各尽不同. ...

  3. C# 关键字extern用法

    C# 关键字extern用法 修饰符用于声明在外部实现的方法.extern 修饰符的常见用法是在使用 Interop 服务调入非 托管代码时与 DllImport 属性一起使用:在这种情况下,该方法还 ...

  4. python模块之HTMLParser之穆雪峰的案例(理解其用法原理)

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #python模块之HTMLParser之穆雪峰的案例(理解其用法原理) #http://www.cnblog ...

  5. extern用法详解(转)

    extern用法详解(转)       1 基本解释 extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义. 另外,extern ...

  6. extern用法详解

    [转]extern用法详解 Posted on 2011-08-16 11:15 单鱼游弋 阅读(98) 评论(0)编辑收藏 1 基本解释 extern可以置于变量或者函数前,以标示变量或者函数的定义 ...

  7. string_view理解与用法(二)

    以前写了<string_View理解与用法(一)>和<详解C++17下的string_view>,请参考. 本篇文章从string_view引入的背景出发,依次介绍了其相关的知 ...

  8. Promise async/await的理解和用法

    Promise && async/await的理解和用法 为什么需要promise(承诺)这个东西 在之前我们处理异步函数都是用回调这个方法,回调嵌套的时候会发现 阅读性 和 调试 的 ...

  9. php yield 个人小解_PHP5.5新特性之yield理解与用法实例分析

    本文实例讲述了PHP5.5新特性之yield理解与用法.分享给大家供大家参考,具体如下: yield生成器是php5.5之后出现的,yield提供了一种更容易的方法来实现简单的迭代对象,相比较定义类实 ...

最新文章

  1. InfluxDb中写入重复数据问题解决方案
  2. Java设计模式(二十三):访问者设计模式
  3. 全栈、均栈、MERN栈,哪个才是下一代Web项目的正确栈
  4. leetcode_two sum()
  5. 在 ubuntu 上编译 qtopia-2.2.0问题
  6. 软件定义数据中心—Windows Server SDDC技术与实践
  7. break lab c语言,C语言实验lab10.doc
  8. 我们前端忙成狗 人家后端写sql?
  9. 信息学奥赛一本通 2019:【例4.4】求阶乘
  10. wp_nav_menu($args)函数说明
  11. 如何在手机上使用TensorFlow
  12. 【ElasticSearch】 ElasticSearch 读取 流程
  13. 动态为GridView控件创建列
  14. 后缀数组(bzoj 1031: [JSOI2007]字符加密Cipher)
  15. 你必须知道:localStorage、sessionStorage 和 Cookie 区别在什么地方
  16. windows 内核进程的优先级_华为鸿蒙 OS 轻量内核设计理念与关键特性
  17. python的遍历循环语句for、不能遍历的数据类型是_14、python循环遍历 for 语法
  18. 松柏先生实地调研浙江名茶“平阳黄汤”传播茶文化
  19. 怎样自制微信gif动态表情包?
  20. 计算机二级题百度云,计算机二级office题库

热门文章

  1. 凸包 —— 五种解法
  2. 【愚公系列】2023年06月 网络安全高级班 027.应急响应溯源分析(Windows⽇志分析)
  3. 计算机基础知识重点汇总
  4. mac查看端口被哪个进程占用并杀死
  5. android文字和图片混排
  6. linux没权限ttl连接后,如何在没有root权限的Linux上从C中的UDP数据包中找回TTL超出的错误消息?...
  7. Vuex配置及Vuex原理图分析,简单明了,一遍就明白
  8. Symbol SWI2_EGU2_IRQHandler multiply defined(by nrf_sdh_freertos.o and nrf_sdh.o)
  9. 如果你是第一次创业,一定要避免犯这6个致命错误
  10. Openlayers View 限制显示范围、限制缩放级别、限制拖动等