声明外部变量
现代编译器一般采用按文件编译的方式,因此在编译时,各个文件中定义的全局变量是互相透明的,也就是说,在编译时,全局变量的可见域限制在文件内部。下面举一个简单的例子。创建一个工程,里面含有A.cpp和B.cpp两个简单的C++源文件:
//A.cppint i;void main(){}
//B.cppint i;
这两个文件极为简单,在A.cpp中定义了一个全局变量i,在B中也定义了一个全局变量i。对A和B分别编译,都可以正常通过编译,但是进行链接的时候,却出现了错误,错误提示如下:
Linking...B.obj : error LNK2005: "int i" (?i@@ 3HA ) already defined in A.objDebug/A.exe : fatal error LNK1169: one or more multiply defined symbols foundError executing link.exe.A.exe - 2 error(s), 0 warning(s)
这就是说,在编译阶段,各个文件中定义的全局变量相互是透明的,编译A时觉察不到B中也定义了i,同样,编译B时觉察不到A中也定义了i。但是在链接阶段,要将各个文件的内容“合为一体”,因此,如果某些文件中定义的全局变量名相同的话,在链接阶段就会报重复定义(one or more multiply defined symbols)的错误。
因此,各个文件中定义的全局变量名不可相同。
在链接阶段,编译产生的obj文件合并了A、B两文件的内容,这也是出现int i重复定义错误的原因。
举个例子
一个文件中定义的全局变量,可以在整个程序的任何地方被使用,举例说,如果A文件中定义了某全局变量,那么B文件中也可以使用该变量。修改我们的程序,加以验证:
//A.cppvoid main(){ i = 100; //试图使用B中定义的全局变量}
//B.cppint i;
出现如下意料之中的编译错误,未定义int i错误(undeclared identifier Error),因为在链接之前A、B文件中的变量是彼此不可见的。
Compiling...A.cpp C:/Documents and Settings/wangjian/桌面/try extern/A.cpp(5) : error C2065: 'i' : undeclared identifierError executing cl.exe.A.obj - 1 error(s), 0 warning(s)
编译器没有能够意识到,某个变量符号虽然不是本文件定义的,但是它可能是在其它的文件中定义的,为了避免错误的发生extern派上用场了。为上面的错误程序加上extern关键字后,顺利通过编译,链接,代码如下:
//A.cppextern int i;void main(){ i = 100; //试图使用B中定义的全局变量}
//B.cppint i;
在C++文件中调用C方式编译的函数
相对于C,C++中新增了诸如重载等新特性,它们的编译有一些重要区别。将下面的小程序分别按C和C++方式编译,来探讨两种编译方式的区别。
int i;int func(int t){ return 0;}void main(){}
以C方式编译的结果如下:
DWORD :PUBLIC _funcPUBLIC _main
以C++方式编译的结果如下:
PUBLIC ?i@@ 3HA ; iPUBLIC ?func@@YAHH@Z ; funcPUBLIC _main
可见,C方式编译下,变量名和函数名之前被统一加上了一个下划线,而C++编译后的结果却复杂的多,i变成了?i@@ 3HA ,func变成了?func@@YAHH@Z。C++中的这种看似复杂的命名规则是为C++中的函数重载,参数检查等特性服务的。
不同编译方式下的函数调用
如果在工程中,不仅有CPP文件,还有以C方式编译的C文件,函数调用就会有一些微妙之处。有如下CPP文件A.CPP和C文件B.C两个文件。
//A.CPPvoid func();void main(){ func();}
//B.Cvoid func(){}
对A.CPP和B.C分别编译,都没有问题,但是链接时出现错误,原因就是C和CPP不同的编译方式产生的冲突。比如在上文中提到,C方式编译下,变量名和函数名之前被统一加上了一个下划线,而C++编译后的结果却复杂的多,i变成了?i@@ 3HA。
Linking...A.obj : error LNK2001: unresolved external symbol "void __cdecl func(void)" (?func@@YAXXZ)Debug/A.exe : fatal error LNK1120: 1 unresolved externalsError executing link.exe.A.exe - 2 error(s), 0 warning(s)
此时,可以通过extern关键字,来帮助编译器解决上面提到的问题。对于本例,只需将A.CPP改成如下代码即可:
//A.CPPextern "C"{ void func(); //引入C语言方式编译的函数或变量}void main(){ func();}