7.6.3 全局变量跨文件引用
这个话题在前面简单说过,在这里,将进行更详细的讲解,因为在实际的工作中,全局变量跨文件引用的情形时有发生。
前面曾经说过,在Visual Studio中,一个项目可以通过编译、链接等步骤最后生成一个可执行文件,在Windows下可执行文件就是扩展名为.exe的文件,而在Linux下可执行文件就是具有可执行权限的文件。
在Visual Studio中,一个项目由一个或者多个源程序文件组成,一般在Visual Studio中,C语言的源程序文件扩展名为.cpp(或.c)。因为目前演示的代码比较简单,因此只用了一个源程序文件(MyProject.cpp)。也可以建立另一个源程序文件,例如现在准备新建立一个叫作MyProject2.cpp的源程序文件。编译的时候,Visual Studio会把这些源程序文件分别编译,最终统一链接成为一个可执行文件。
现在,看看如何创建一个MyProject2.cpp源程序文件:
(1)在Visual Studio中双击图7.13中左侧的MyProject.cpp,先把这个源程序文件打开,其内容展现在屏幕的右侧。
图7.13 打开当前的源程序文件
(2)在图7.13中,看右上侧,显示着所打开的源程序文件的文件名标签页,右击这个标签页,并在弹出的快捷菜单中选择“打开所在的文件夹”命令,这样,就打开了MyProject.cpp文件所在的目录,如图7.14所示,其实这与用文件资源管理器打开该文件所在的目录具有完全相同的效果。
图7.14 在资源管理器中打开MyProject.cpp文件所在的目录
(3)直接用Ctrl+C、Ctrl+V快捷键复制和粘贴MyProject.cpp,并把新复制出来的文件改名为MyProject2.cpp,看起来如图7.15所示。
图7.15 在资源管理器中通过MyProject.cpp复制出MyProject2.cpp文件
(4)现在需要把MyProject2.cpp加入到当前的Visual Studio项目中。如何加入?看图7.13左侧,观察到MyProject.cpp文件是在一个叫作“源文件”的文件夹下,这其实是一个虚拟文件夹(也就是在真实的文件目录中并不存在,只是在Visual Studio中创建出来的,用于方便文件的分类管理),只要把MyProject2.cpp加入到该文件夹下即可,右击“源文件”文件夹并在弹出的快捷菜单中选择“添加”→“现有项”命令,如图7.16所示。
图7.16 往一个项目中增加一个已经存在的.cpp源程序文件
图7.17 MyProject2.cpp源程序文件被增加到项目MyProject中
(5)在弹出的对话框中选择MyProject2.cpp并单击“添加”按钮,就可以把MyProject2.cpp文件添加到“源文件”文件夹中,这也就相当于把文件添加到项目中了,如图7.17所示。
现在整个项目(工程)就包括MyProject.cpp和MyProject2.cpp两个源程序文件了,因为MyProject2.cpp是从MyProject.cpp中复制过来的,而且一个项目中只能有一个main函数,所以,要把MyProject2.cpp中一些额外的代码行删一删。这里为了简单,只保留带#include行的语句即可,如图7.18所示。
图7.18 当前MyProject2.cpp文件中只有一行语句
编译一下整个项目(选择“生成”→“重新生成解决方案”命令即可实现编译),看到了屏幕下方提示编译成功(全部重新生成:成功1个),如图7.19所示。
图7.19 成功编译一个项目的提示
那么如果在MyProject2.cpp中定义一个全局变量,能否在MyProject.cpp中引用呢?
在MyProject2.cpp中增加如下代码行:
在MyProject.cpp的main函数中增加如下代码行:
此时再次编译整个项目(选择“生成”→“重新生成解决方案”命令),会发现报错,系统大概会提示类似如下这样的错误:
errorC2065:“g_a”:未声明的标识符。
这说明在MyProject.cpp中,并不认识这个全局变量g_a(因为这个全局变量是在MyProject2.cpp中定义的)。
还记得前面在介绍关键字extern时是这样说的:如果某个函数想引用在它后面定义的全局变量,则可以使用关键字extern做一个“外部变量说明”,表示该变量在函数的外部定义,这样在函数内就能使用,否则编译就会出错。那如果想在一个源程序文件中引用另外一个源程序文件中定义的全局变量,也可以用extern关键字,在引用该全局变量的源程序文件的开头做一个“外部变量说明”即可,以表明在本文件中出现的变量是一个已经在其他源程序文件中定义过的外部变量,本文件不必为它再分配内存。
一般来说,一个全局变量的作用域是从它定义的点到整个源程序文件结束,但是,通过使用extern,将它的作用域扩大到了有extern说明的其他源程序文件。如果再有更多的源程序文件中要引用这个全局变量,也要在这些源程序文件的开头用extern来说明这个全局变量为外部全局变量。
为什么要把extern放在源程序文件的开头?因为在哪行用了extern关键字,那么哪行之后的代码行才能够引用这个全局变量,否则在编译的时候系统依旧会提示“未声明的标识符”之类的错误。
现在,在MyProject.cpp的开头(在#include语句后面)增加如下代码行:
这样,就可以再次用Ctrl+F5键(或选择“调试”→“开始执行(不调试)”命令)来编译并执行程序,会得到如下执行结果,这样就实现了跨文件引用全局变量。
特别注意,上一节讲解extern关键字时,extern关键字也是可以用在某个函数内部的,如7.5节所举的例子:
而现在这种跨文件使用全局变量的extern关键字用法,一般都是要求将extern说明放在源程序文件的开头(一般位于#include语句行之后)。另外,使用这种跨文件的全局变量要很小心,因为在某个源程序文件的某个函数中改变了这些全局变量的值,也会影响到其他源程序文件中使用该全局变量值的函数。
现在可能也有这样一个需求:希望某些全局变量,只能在本源程序文件中被使用,不想被其他源程序文件进行跨文件引用,那也很简单,在定义这个全局变量时在最前面加上static关键字。例如:
这个时候如果其他文件再使用externint g_a;时,编译时就会出现诸如“无法解析的外部符号”之类的错误提示,读者可以自己测试并观察编译结果。
这样做有个优点:如果在MyProject.cpp和MyProject2.cpp中定义了两个相同名字的全局变量,那么如果不加static则编译链接时Visual Studio会报诸如“全局变量被重定义”的错误,但如果在定义这两个全局变量时都在前面加了static关键字,则自己的源程序文件用自己文件里定义的全局变量,互相不受影响,编译链接时也不会再报错。