深⼊理解extern的⽤法以及声明和定义的区别
⼀、英语学习
extern adj. 外⾯的;外来的;对外的
⼆、c++中定义和声明的区别 (博⽂1)
⼀.基本理解
1.定义和声明的简单说明
a.声明就是指给除了当前变量或者函数,或者类什么的名字,不给其中的内容,就是先告诉你有这样⼀个什么类型的变量或者函数,但是这个变量或者函数的具体信息却是不知道的。就好⽐跟你介绍⼀个⼈的时候,声明就是只告诉你这个⼈叫什么,但是缺不给你说这个⼈到底怎么样,他有哪些优点,缺点,喜好问题是什么的。
b.定义就不⼀样了,定义直接告诉你了所有的东西,这个变量是什么,这个函数是什么功能,这个类⾥⾯包含了什么东西。很具体的说明。
2.对于变量来说
a.定义:可以为变量分配存储空间,并且可以给变量⼀个初始值
b.声明:告诉编译器这个变量的名字和类型(extern int a;(在没有赋值的情况下,变量前加上关键字extern⼀定为声明))
⽰例:
extern int i; //声明,不是定义
int i; //声明,也是定义
3.对于函数来说
a.定义:就是这个函数具体的实现
b.声明:告诉编译器在这个程序中会有这么⼀个函数
简单来说,如果函数带有{},则其为定义;否则,就为声明。
⼆.深⼊探讨
1.在⼀个程序中只能对变量定义⼀次,因为我们不能让编译器⼀直为同⼀个变量,函数分配不同的存储空间;⽽可以对变量进⾏很多次的声明。
2.在任何多⽂件中使⽤的变量都需要有与定义分离的声明。在这种情况下,⼀⽂件含有变量的定义,则使⽤该变量的其他⽂件中就要含有该变量的声明,⽽不是定义。
3.在头⽂件中不能放变量的定义,⼀般存放变量的声明。因为头⽂件要被其他⽂件包含,如果放到头⽂件当中就不能避免变量被多次定义。(const,inline)
三、深⼊理解extern⽤法
1、声明extern关键字的全局变量和函数可以使得它们能够跨⽂件被访问。
如:在a.cpp中使⽤b.cpp中的变量
2、全局函数的声明语句中,关键字extern可以省略,因为全局函数默认是extern类型的。
static修饰的变量实现⽅法⼀:在a.cpp中使⽤b.cpp中的变量
我们⼀般把所有的全局变量和全局函数的实现都放在⼀个*.cpp⽂件⾥⾯,然后⽤⼀个同名的*.h⽂件包含所有的函数和变量的声明。如:
/*Demo.h*/
#pragma once
extern int a;
extern int b;
int add(int a,int b);
#include "Demo.h" /*这句话写或者不写在本例中都⾏,不过建议不写*/
/*不写不会出问题,写了有些情况下会出问题,下⾯有解释*/
int a =10;
int b =20;
int add(int l,int r)
{
return l +r;
}
/* main.cpp */
#include <stdio.h>
#include "Demo.h"
int main()
{
int c=add(1,2);
printf("main.c: The a is %d\n", a);
printf("main.c: The b is %d\n", b);
printf("main.c: The c is %d\n", c);
return 0;
}
实现⽅法⼆:在主函数中声明extern也是可以实现在a.cpp中使⽤b.cpp中的变量
/*Demo.h*/
#pragma once
//下⾯两句话如果写了就是重定义
//int a;
//int b;
int add(int a, int b);
/*Demo.cpp*/
#include "Demo.h" /*这句话写或者不写在本例中都⾏,不过建议不写*/
/
*不写不会出问题,写了有些情况下会出问题,下⾯有解释*/
//注意点1:如果在demo.h中定义过a 和b ,下⾯这两⾏这样写也是会报错的,
//a = 10;
//b = 20;
int a = 10;
int b = 20;
int add(int l, int r)
{
return l + r;
}
#include <stdio.h>
#include "Demo.h"
extern int a;
extern int b;
int main()
{
int c=add(1,2);
printf("main.c: The a is %d\n", a);
printf("main.c: The b is %d\n", b);
printf("main.c: The c is %d\n", c);
}
四、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及其它语⾔的混合编程:
被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不可以,只允许对象本⾝⽤它。具体差别⾸先,static与extern是⼀对“⽔⽕不容”的家伙,也就是说extern和static不能同时修饰⼀个变量;其次,static修饰的全局变量声明与定义同时进⾏,也就是说当你在头⽂件中使⽤static声明了全局变量后,它也同时被定义了;最后,static修饰全局变量的作⽤域只能是本⾝的编译单元,也就是说它的“全局”只对本编译单元有效,其他编译单元则看不到它,如:
/*test1.h*/
#ifndef TEST1H
#define TEST1H
static 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”。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论