深入理解extern使用方法

深入理解extern使用方法

大家好,又见面了,我是全栈君。

一、 extern做变量声明

l  声明externkeyword的全局变量和函数可以使得它们可以跨文件被訪问。

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

/*Demo.h*/
#pragma  once
extern inta;
extern intb;
intadd(inta,intb);
/*Demo.cpp*/
#include "Demo.h" /*这句话写或者不写在本例中都行,只是建议不写*/
/*不写不会出问题,写了有些情况下会出问题,以下有解释*/
int a =10;
int b =20;
 
int add(intl,intr)
{
      return l +r;
}


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

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

 

l  全局函数的声明语句中,keywordextern能够省略,由于全局函数默认是extern类型的。

l 声明和定义

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

externchar 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++语言中表明函数和全局变量作用范围(可见性)的keyword,该keyword告诉编译器,其声明的函数和变量能够在本模块或其他模块中使用。

记住。下列语句:

extern int a;

不过一个变量的声明,其并非在定义变量a,并未为a分配内存空间。变量a在全部模块中作为一种全局变量只能被定义一次。否则会出现连接错误。

通常,在模块的头文件里对本模块提供给其他模块引用的函数和全局变量以keywordextern声明。

比如,假设模块B欲引用该模块A中定义的全局变量和函数时仅仅需包括模块A的头文件就可以。这样。模块B中调用模块A中的函数时,在编译阶段,模块B尽管找不到该函数,可是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。

与extern相应的keyword是static,被它修饰的全局变量和函数仅仅能在本模块中使用。因此。一个函数或变量仅仅可能被本模块使用时。其不可能被extern “C”修饰。

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

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

l  extern “C”的惯使用方法

(1)在C++中引用C语言中的函数和变量,在包括C语言头文件(如果为cExample.h)时。需进行下列处理:

extern “C”{

#include “cExample.h”

}

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

(2)在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不能够,仅仅同意对象本身用它。详细区别首先,staticextern是一对“水火不容”的家伙,也就是说externstatic不能同一时候修饰一个变量。其次。static修饰的全局变量声明与定义同一时候进行。也就是说当你在头文件里使用static声明了全局变量后。它也同一时候被定义了;最后,static修饰全局变量的作用域仅仅能是本身的编译单元,也就是说它的“全局”仅仅对本编译单元有效,其它编译单元则看不到它,如:

/*test1.h*/#ifndef TEST1H#define TEST1Hstatic char g_str[]="123456";void fun1();#endif
/*test1.cpp*/#include "test1.h"void fun1(){      cout <<g_str<<endl;}

/*test2.cpp*/
#include "test1.h"
void fun2()
{
      cout <<g_str<<endl;
}


以上两个编译单元能够连接成功。当你打开test1.obj时,你能够在它里面找到字符串”123456″。同一时候你也能够在test2.obj中找到它们。它们之所以能够连接成功而没有报反复定义的错误是由于尽管它们有同样的内容,可是存储的物理地址并不一样,就像是两个不同变量赋了同样的值一样。而这两个变量分别作用于它们各自的编译单元。

或许你比較较真,自己偷偷的跟踪调试上面的代码,结果你发现两个编译单元(test1,test2)的g_str的内存地址同样。于是你下结论static修饰的变量也能够作用于其它模块,可是我要告诉你,那是你的编译器在欺骗你,大多数编译器都对代码都有优化功能,以达到生成的目标程序更节省内存,运行效率更高,当编译器在连接各个编译单元的时候,它会把同样内容的内存仅仅拷贝一份,比方上面的”123456″。位于两个编译单元中的变量都是同样的内容,那么在连接的时候它在内存中就仅仅会存在一份了,假设你把上面的代码改成以下的样子,你立即就能够拆穿编译器的谎言:

/*test1.cpp*/
#include "test1.h"
void fun1()
{
      g_str[0]=''a'';
      cout <<g_str<<endl;
}
/*test2.cpp*/
#include "test1.h"
void fun2()
{
      cout <<g_str<<endl;
}

/*main.cpp*/void main(){      fun1();// a23456      fun2();// 123456}


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

四、extern和const

C++中const修饰的全局常量具有跟static同样的特性,即它们仅仅能作用于本编译模块中,且static修饰的是全局变量,可是const能够与extern连用来声明该常量能够作用于其它编译模块中,如externconst 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″。

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/116224.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • DB2 递归查询_oracle递归查询所有子节点

    DB2 递归查询_oracle递归查询所有子节点以父子节点定义的表数据递归查询:DB2 递归查询WITH temp(id,parentid,level)    AS   ( SELECT id,parentid,0   FROM AMIS_WF_NODE WHERE parentid = ‘4028e48146a3c33d0146a3cd9e860022’  UNION ALL   SELECT b.id,b.pare

    2022年10月27日
  • Ora:12154 PLsql连接报错

    Ora:12154 PLsql连接报错新入职公司,需要安装一些软件,安装了oracle客户端和plsql,结果发现plsql的database是空白,没有可选。找了度娘,查看安装路径,是oracle安装路径:d:/oracle/product/11.2.0/client_1/admin文件夹下少了两个文件,listener.ora和tnsnames.ora,于是卸载重装,折腾了几次,连注册表都删了,还是不行。解决方法:在admin路径栏敲cmd,netca,根据提示新建一个监听。admin文件夹下就会多一个listener.ora文件(可能

  • php三个数从大到小排列_php常用的流程控制语句

    php三个数从大到小排列_php常用的流程控制语句<?php$a = rand(100,999);$b = rand(100,999);$c = rand(100,999);echo “a=”.”$a”.”<br>”;echo “b=”.”$b”.”<br>”;echo “c=”.”$c”.”<br>”;if(($a > $b ) && ($a > …

  • docker导出所有镜像_怎么把docker镜像复制出来

    docker导出所有镜像_怎么把docker镜像复制出来背景由于工作需要,要一次性导出机器上全部的镜像。导出命令:dockersave$(dockerimages–format'{{.Repository}}:{{.Tag}}’)-oallinone.tar

  • 怎么卸载安装的linux系统_如何卸载linux系统

    怎么卸载安装的linux系统_如何卸载linux系统一、jdk-6u45-linux-x64-rpm.bin安装和卸载安装:二、jdk-6u45-linux-x64.bin安装和卸载三、.tar.gz/.tgz、*.bz2形式的二进制软件包安装和卸载

  • cdn加速配置_自己配置cdn

    cdn加速配置_自己配置cdn配置方法同上篇文章一样CDN加速配置,阿里云cdn内配置好以后即可剩下把前端服务器nginx内的证书文件上传以及配置写好即可。后端服务器也同上配置证书下载上传服务器配置好nginx即可。如果都配置好以后,不可以访问时,注意查看前端请求接口是否为https方式。如果为http时,检查是否更新服务器成功,如果成功后还不可以正常访问时,前端修改代码如下:vue.config.js内添加下行代码。index.html下添加代码:https:true完毕!…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号