} } }

    C++全局变量的声明和定义

    添加时间:2013-7-12 点击量:

    C++全局变量的声明和定义


    (1)编译单位(模块)

      在VC或VS上编写完代码,点击编译按钮筹办生成exe文件时,编译器做了两步工作:

      第一步,将每个.cpp(.c)和响应的.h文件编译成obj文件;

      第二步,将中所有的obj文件进行LINK,生成终极.exe文件。

     

      那么,错误可能在两个处所产生:

      一个,编译时的错误,这个主如果语法错误;

      一个,链接时的错误,主如果反复定义变量等。

        

      编译单位指在编译阶段生成的每个obj文件。

      一个obj文件就是一个编译单位。

      一个.cpp(.c)和它响应的.h文件共同构成了一个编译单位。

      一个由很多编译单位构成,每个obj文件里包含了变量存储的相对地址等。




    (2)声明与定义

        函数或变量在声明时,并没有给它实际的物理内存空间,它有时辰可包管你的法度编译经由过程;

        函数或变量在定义时,它就在内存中有了实际的物理空间。

     

        若是你在编译单位中引用的外部变量没有在全部中任何一个处所定义的话,那么即使它在编译时可以经由过程,在连接时也会报错,因为法度在内存中找不到这个变量。

     

        函数或变量可以声明多次,但定义只能有一次。




    (3) extern感化

        感化一:当它与C一路连用时,如extern C void fun(int a, int b);,则编译器在编译fun这个函数名时按C的规矩去翻译响应的函数名而不是C++的。

        感化二:当它不与C在一路润饰变量或函数时,如在头文件中,extern int g_nNum;,它的感化就是声明函数或变量的感化局限的关键字,其声明的函数和变量可以在本编译单位或其他编译单位中应用。

     

        即B编译单位要引用A编译单位中定义的全局变量或函数时,B编译单位只要包含A编译单位的头文件即可,在编译阶段,B编译单位固然找不到该函数或变量,但它不会报错,它会在链接时从A编译单位生成的目标代码中找到此函数。

     


    (4)全局变量(extern)
        有两个类都须要应用共同的变量,我们将这些变量定义为全局变量。比如,res.h和res.cpp分别来声明和定义全局变量,类ProducerThread和ConsumerThread来应用全局变量。(以下是QT代码)




    /res.h声明全局变量/
    
    #pragma once

    #include
    <QSemaphore>

    const int g_nDataSize = 1000; // 临盆者临盆的总数据量
    const int g_nBufferSize = 500; // 环形缓冲区的大小

    extern char g_szBuffer[]; // 环形缓冲区
    extern QSemaphore g_qsemFreeBytes; // 把握环形缓冲区的余暇区(指临盆者还没填充数据的区域,或者花费者已经读取过的区域)
    extern QSemaphore g_qsemUsedBytes; // 把握环形缓冲区中的应用区(指临盆者已填充数据,但花费者没有读取的区域)
    /
    /



    上述代码中g_nDataSize、g_nBufferSize为全局常量,其他为全局变量。







    /res.cpp定义全局变量/
    
    #pragma once
    #include
    res.h

    // 定义全局变量
    char g_szBuffer[g_nBufferSize];
    QSemaphore g_qsemFreeBytes(g_nBufferSize);
    QSemaphore g_qsemUsedBytes;
    //





    在其他编译单位中应用全局变量时只要包含其地点头文件即可。







    /类ConsumerThread应用全局变量/
    
    #include
    consumerthread.h
    #include
    res.h
    #include
    <QDebug>

    ConsumerThread::ConsumerThread(QObject
    parent)
    : QThread(parent) {

    }

    ConsumerThread::ConsumerThread() {

    }

    ConsumerThread::
    ~ConsumerThread() {

    }

    void ConsumerThread::run() {
    forint i = 0; i < g_nDataSize; i++) {
    g_qsemUsedBytes.acquire();
    qDebug()
    <<Consumer <<g_szBuffer[i % g_nBufferSize];
    g_szBuffer[i
    % g_nBufferSize] = ;
    g_qsemFreeBytes.release();

    }
    qDebug()
    <<&&Consumer Over;
    }
    //






        也可以把全局变量的声明和定义放在一路,如许可以防止忘怀了定义,如上方的extern char g_szBuffer[g_nBufferSize]; 然后把引用它的文件中的#include res.h换成extern char g_szBuffer[];。

        然则如许做很不好,因为你无法应用#include res.h(应用它,若达到两次及以上,就呈现重定义错误;注:即使在res.h中加#pragma once,或#ifndef也会呈现反复定义,因为每个编译单位是零丁的,都邑对它各行其是定义),那么res.h声明的其他函数或变量,你也就无法应用了,除非也都用extern润饰,如许太麻烦,所以还是推荐应用.h中声明,.cpp中定义的做法。




    (5)静态全局变量(static)

        重视应用static润饰变量,就不克不及应用extern来润饰,即static和extern不成同时呈现。

        static润饰的全局变量的声明与定义同时进行,即当你在头文件中应用static声了然全局变量,同时它也被定义了。

        static润饰的全局变量的感化域只能是本身的编译单位。在其他编译单位应用它时,只是简单的把其值复制给了其他编译单位,其他编译单位会别的开个内存保存它,在其他编译单位对它的批改并不影响本身在定义时的值。即在其他编译单位A应用它时,它地点的物理地址,和其他编译单位B应用它时,它地点的物理地址不一样,A和B对它所做的批改都不克不及传递给对方。

        多个处所引用静态全局变量地点的头文件,不会呈现重定义错误,因为在每个编译单位都对它开辟了额外的空间进行存储。





    以下是Windows把握台应用法度代码示例:



    /res.h/
    
    static char g_szBuffer[6] = 12345;
    void fun();
    //








    /res.cpp/
    
    #include
    res.h
    #include
    <iostream>
    using namespace std;

    void fun() {
    forint i = 0; i < 6; i++) {
    g_szBuffer[i]
    = A + i;
    }
    cout
    <<g_szBuffer<<endl;
    }
    //



    /test1.h/
    
    void fun1();
    //











    /test1.cpp/
    
    #include
    test1.h
    #include
    res.h
    #include
    <iostream>
    using namespace std;

    void fun1() {
    fun();

    forint i = 0; i < 6; i++) {
    g_szBuffer[i]
    = a + i;
    }
    cout
    <<g_szBuffer<<endl;
    }
    //









    /test2.h/
    
    void fun2();
    //









    /test2.cpp/
    
    #include
    test2.h
    #include
    res.h
    #include
    <iostream>
    using namespace std;

    void fun2() {
    cout
    <<g_szBuffer<<endl;
    }
    //









    /main.cpp/
    
    #include
    test1.h
    #include
    test2.h

    int main() {
    fun1();
    fun2();

    system(
    PAUSE);
    return 0;
    }
    //





    运行成果如下:


                      



        按我们的直观印象,认为fun1()和fun2()输出的成果都为abcdef,可实际上fun2()输出的确是初始值。然后我们再跟踪调试,发明res、test1、test2中g_szBuffer的地址都不一样,分别为0 x0041a020、0 x0041a084、0 x0041a040,这就说了然为什么不一样。

     

        注:一般定义static 全局变量时,都把它放在.cpp文件中而不是.h文件中,如许就不会给其他编译单位造成不须要的信息污染。

     







    (6)全局常量(const)

        const零丁应用时,其特点与static一样(每个编译单位中地址都不一样,不过因为是常量,也不克不及批改,所以就没有多大关系)。

        const与extern一路应用时,其特点与extern一样。










    extern const char g_szBuffer[];      //写入 .h中
    
    const char g_szBuffer[] = 123456; // 写入.cpp中








    文艺不是炫耀,不是花哨空洞的文字堆砌,不是一张又一张的逆光照片,不是将旅行的意义转化为名牌包和明信片的物质展示;很多时候它甚至完全不美——它嘶吼、扭曲,它会痛苦地抽搐,它常常无言地沉默。——艾小柯《文艺是一种信仰》
    分享到: