全局变量|extern |static|const 用法小结

1,声明与定义的区别

       函数或变量在声明时,并没有给它实际的物理内存空间只有当函数或变量定义的时候,才在内存中分配实际的物理空间。声明有时可以保证程序编译通过,但是,如果你在编译模块中引用的外部变量没有在整个工程中任何一个地方定义的话,那么即使它在编译时可以通过,在连接时也会报错,因为程序在内存中找不到这个变量。对同一个变量或函数的声明可以有多次,而定义只能有一次!(我一般在头文件中声明,在.c/.cpp文件中定义)。

2,全局变量

      全局变量:在函数外定义,存储在静态存储区,在程序运行期间都不释放。全局变量的作用域为从声明或定义处开始,到文件结束。当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问(不能直接访问,需要先使用extern声明才能访问)。

3,extern  

      extern有两个作用。

      第一个,当它与"C"一起连用时,如: extern "C" void fun(int a, int b); 则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++ C++的规则在翻译这个函数名时会把fun这个名字变得面目全非,可能是fun@aBc_int_int#%$也可能是别的不同的编译器采用的方法不一样(原因是:C++支持函数的重载)。

     第二种,当extern不与"C"在一起修饰变量或函数时,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块或者其他模块中使用记住它是一个声明不是定义!也就是说B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可,在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。

     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 改为
    extern char g_str[] = "123456"; // 这个时候相当于没有 extern
    然后把 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 修饰,这样的话你光声明的函数就要一大串,而且头文件的作用就是要给外部提 供接口使用的,所以 请记住, 只在头文件中做声明,真理总是这么简单

4,static

        static分配在静态存储区, 在程序整个运行期间都不释放。staticextern是一对水火不容的家伙,也就是说externstatic不能同时修饰一个变量;其次,static饰的全局变量声明与定义同时进行,也就是说当你在头文件中使用static声明了全局变量后,它也同时被定义了;最后,static修饰全局变量的作用域只能是本身的编译单元,也就是说它的全局只对本编译单元有效,其他编译单元则看不到它

(1)static局部变量

      static局部变量在所处模块在初次运行时进行初始化工作, 且只操作一次。对于局部静态变量, 如果不赋初值, 编译期会自动赋初值0或空字符。

   

(2)外部静态变量/函数

        用来表示不能被其它文件访问的全局变量和函数。, 但为了限制全局变量/函数的作用域, 函数或变量前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。注意此时, 对于外部(全局)变量, 不论是否有static限制, 它的存储区域都是在静态存储区, 生存期都是全局的. 此时的static只是起作用域限制作用, 限定作用域在本模块(文件)内部.
使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。
示例程序三:
 

//file1.cpp

static int varA;
int varB;
extern void funA()
{
……
}

static void funB()
{
……
}

//file2.cpp

extern int varB; // 使用file1.cpp中定义的全局变量
extern int varA; // 错误! varA是static类型, 无法在其他文件中使用
extern vod funA(); // 使用file1.cpp中定义的函数
extern void funB(); // 错误! 无法使用file1.cpp文件中static函数

 

(3)静态数据成员/成员函数(C++特有)
C++重用了这个关键字,并赋予它与前面不同的第三种含义:表示属于一个类而不是属于此类的任何特定对象的变量和函数. 这是与普通成员函数的最大区别, 也是其应用所在, 比如在对某一个类的对象进行计数时, 计数生成多少个类的实例, 就可以用到静态数据成员. 在这里面, static既不是限定作用域的, 也不是扩展生存期的作用, 而是指示变量/函数在此类中的唯一性. 这也是”属于一个类而不是属于此类的任何特定对象的变量和函数”的含义. 因为它是对整个类来说是唯一的, 因此不可能属于某一个实例对象的. (针对静态数据成员而言, 成员函数不管是否是static, 在内存中只有一个副本, 普通成员函数调用时, 需要传入this指针, static成员函数调用时, 没有this指针. )
请看示例程序四(<effective c++ (2nd)>(影印版)第59页)

class EnemyTarget {
public:
  EnemyTarget() { ++numTargets; }
  EnemyTarget(const EnemyTarget&) { ++numTargets; }
  ~EnemyTarget() { --numTargets; }
  static size_t numberOfTargets() { return numTargets; }
  bool destroy();   // returns success of attempt to destroy EnemyTarget object
private:
  static size_t numTargets;               // object counter
};
// class statics must be defined outside the class;
// initialization is to 0 by default
size_t EnemyTarget::numTargets;

 另外, 在设计类的多线程操作时, 由于POSIX库下的线程函数pthread_create()要求是全局的, 普通成员函数无法直接做为线程函数, 可以考虑用Static成员函数做线程函数.

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


5,const

       const修饰的全局常量据有跟static相同的特性(有条件的,感谢sswv的提醒,const放在只读静态存储区),即它们只能作用于本编译模块中,但是const可以与extern连用来声明该常量可以作用于其他编译模块中,



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值