解析C++如何定义全局变量

樵夫2021-12-16 14:15

  让我们来看看以下全局变量/常量的区别:

  1、用extern修改的全局变量。

  上面已经提到了extern的作用,下面我们举个例子,比如:

  test1、h中有以下声明:

    #ifndef TEST1H
    #define TEST1H
    extern char g_str[]; // 声明全局变量g_str
    void fun1();
    #endif
    在test1.cpp中
    #include "test1.h"
   
    char g_str[] = "123456"; // 定义全局变量g_str
   
    void fun1()
    {
        cout << g_str << endl;
    }

  以上是test1模块,可以编译和连接。如果我们还有test2模块,想用g_str,只需要在原文件中引用即可。

 #include "test1.h"

    void fun2()
    {
        cout << g_str << endl;
    }

  以上test1和test2可以同时编译和连接。有兴趣可以用ultraedit打开test1、obj,里面可以放“123456”字符串,但是test2、obj找不到。这是因为g_str是整个项目的全局变量,内存中只有一个。test2、obj不需要再有一个。

  有些人喜欢把全局变量的声明和定义放在一起,这样可以防止忘记定义,比如把上面的test1、h改成。

  externcharg_str[]=“123456”

  然后删除test1、cpp中g_str的定义。此时,当编译连接test1和test2时,将报告连接错误。这是因为您将全局变量g_str的定义放在头文件后面。test1、cpp包含test1、h,因此定义g_str一次,test2、cpp也包含test1、h,因此g_str再次定义。此时,连接器在连接test1和test2时发现了两个g_str。如果您必须将g_str的定义放在test1、h中,请删除test2代码中的#include“test1、h”并更改为:

extern char g_str[];
    void fun2()
    {
        cout << g_str << endl;
    }

  此时,编译器知道g_str是一个来自外部的编译模块,不会在这个模块中重复定义一个,但我想说这是非常糟糕的,因为你不能在test2、cpp中使用#include“test1、h”,所以你不能使用test1、h中声明的其他函数,除非它们也用extern修改,所以你只需要一大串声明函数,头文件的作用是为外部提供界面,所以请记住,只有在头文件中,真理总是那么简单。

  2、用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;
    }
   
    void main()
    {
        fun1(); // a23456
        fun2(); // 123456
    }

  此时,当您跟踪代码时,您会发现两个编译单元中的g_str地址不同,因为您在一个地方修改了它,因此编译器被迫恢复内存的原始外观,并在两个模块中复制两个变量。

  正是因为static具有上述特性,所以在定义static全局变量时,一般都是放在原文件中而不是头文件中,这样就不会对其他模块造成不必要的信息污染。记住这个原则!

  3、全局常量const修饰。

  const修改的全局常量应用广泛。例如,软件中的错误信息字符串是由全局常量定义的。const修改的全局常量与static具有相同的特性,即它们只能作用于本编译模块,但const可以与extern连接,声明该常量可以作用于其他编译模块,如。

  externconstcharg_str[];

  然后不要忘记原文件中的定义:

  constcharg_str[]=“123456”;

  因此,当const单独使用时,它与static相同,当与extern合作时,它的特性与extern相同!因此,我对const没有太多的描述。我只是想提醒你,constchar*g_str=“123456”不同于constcharg_str[]=“123465”。前面的const装饰char*而不是g_str,它的g_str不是常量,被认为是定义的全局变量(可以用于其他编译单元)。因此,如果你喜欢让char*g_str遵守const全局常量规则,最好这样定义constchar*constr“123456”。

  以上就是小编为大家整理发布的“解析C++如何定义全局变量”一文,更多相关内容尽在开课吧广场C++教程频道。

免责声明:本站所提供的内容均来源于网友提供或网络搜集,由本站编辑整理,仅供个人研究、交流学习使用。如涉及版权问题,请联系本站管理员予以更改或删除。
有用
分享