深入理解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不能够,仅仅同意对象本身用它。详细区别首先,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"。

上一篇:POJ1390 Blocks (区间DP)


下一篇:ssh 互通脚本