深入理解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″。

 

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 手机安装busybox教程(去除busybox的xbin连接)

    Busybox,大家应该有所耳闻,其中集成了众多linux的命令和工具。有时候在手机上想使用find,grep等命令但是手机不支持。这时可以在手机上安装busybox.使用方法:1.确定手机型号cpu的指令集,cat/proc/cpuinfo如armv7,armv4,armv6,去网上下载二进制busybox文件。下载地址:http:

    2022年4月12日
    174
  • origin 2018安装教程与安装包

    origin 2018安装教程与安装包百度网盘地址:链接:https://pan.baidu.com/s/11Lo7R4YvldtkcKZxb5vbXQ提取码:aq8o这里是安装包大概1.8G,比较大。如果链接失效可以联系我913121976@qq.com邮箱。有什么问题可以一起交流!1、打开安装文件夹里的setup,进行安装。点击下一步2、同意许可,下一步。点击下一步3、客户信息,前两个可以随便填写,第三个…

    2022年4月27日
    90
  • 可控制转速CSS3旋转风车特效

    以前制作网页动画一般使用javascript,现在已经有越来越多动动画使用纯CSS实现,并且动画的控制也可以使用CSS3实现,因为CSS3来了,CSS3的动画功能确实强大。以下是一个纯CSS3制作

    2021年12月28日
    30
  • SSL协议工作过程

    SSL协议工作过程SSL握手协议:在SSL记录协议之上,会话层之下,在实际的数据传输开始前,协商加密算法、交换密钥、身份认证1.客户端浏览器发送“hello”信息,表示要和网站建立安全SSL连接2.网站服务器响应客户端请求,发给客户端两样东西:网站服务器自己的证书(内含网站的公钥)、一个随机值3.客户端浏览器验证网站服务器证书是否可信4.客户端利用网站服务器发的随机值生成会话密钥5.客户端…

    2022年6月2日
    38
  • Java入门代码_java编程自学网

    Java入门代码_java编程自学网首先在配置好java环境的前提下,安装好eclipse,以下示例均在eclipse下运行,代码详解看注释一、HelloWorld示例代码:packagecom.hpe.java;//这是一个问好程序publicclassHello{//一个类只能有一个main方法publicstaticvoidmain(Stringarg[]){System.out.print(“hellowo…

    2022年10月17日
    0
  • 【stm32f407】SPI实验 驱动W25Q128「建议收藏」

    一.SPI介绍SPI是英语SerialPeripheralinterface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚…

    2022年4月7日
    40

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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