官术网_书友最值得收藏!

2.12 作用域和生命期

作用域是指標(biāo)識(shí)符在程序中的有效范圍。生命期是指標(biāo)識(shí)符在程序中的生存周期,也就是在程序運(yùn)行過(guò)程中,標(biāo)識(shí)符在內(nèi)存中存在的時(shí)間。這里的標(biāo)識(shí)符包括變量名、函數(shù)名、常量名、對(duì)象名、語(yǔ)句標(biāo)號(hào)、宏名等。

2.12.1 作用域

C++的作用域大致可以分為全局作用域、局部作用域和文件作用域三種。還有一種更細(xì)的分法,按照作用域范圍,作用域從大到小分為程序作用域、文件作用域、類作用域、函數(shù)作用域和塊作用域5種類型。

(1)程序作用域

程序作用域是指一個(gè)標(biāo)識(shí)符在整個(gè)程序范圍內(nèi)有效。若一個(gè)程序由多個(gè)文件組成,具有這種作用域的標(biāo)識(shí)符可以在該程序的各個(gè)文件中應(yīng)用。具有程序作用域的標(biāo)識(shí)符只能在一個(gè)文件中定義1次,但要在使用它的文件中用extern聲明。例如,如果有10個(gè)文件都要用到某個(gè)變量,這個(gè)變量也只能在1個(gè)文件中定義,在另外9個(gè)文件中必須用extern聲明后才能使用。

(2)文件作用域

文件作用域是指在一個(gè)文件中所有函數(shù)定義之外定義的名字(包括函數(shù)名),其有效范圍為從定義它的語(yǔ)句位置開(kāi)始,直到文件結(jié)束。具有文件作用域的名字只能在定義它的文件中使用,但不能在組成同一程序的其他文件中使用。

(3)函數(shù)作用域

函數(shù)作用域是指在函數(shù)范圍內(nèi)的任何語(yǔ)句位置都有效。語(yǔ)句標(biāo)號(hào)是唯一具有函數(shù)作用域的標(biāo)識(shí)符。

(4)塊作用域

寫(xiě)在{}內(nèi)的一條或多條語(yǔ)句就構(gòu)成了一個(gè)語(yǔ)句塊,在其中定義的標(biāo)識(shí)符就只能在這對(duì)“{ }”中使用,而且只在定義(或聲明)它的語(yǔ)句位置到離它最近的“}”之間有效,即只能在這段代碼區(qū)域內(nèi)引用它,這就是塊作用域。

在C++中,任何在“{ }”中定義或聲明的標(biāo)識(shí)符都具有塊作用域。局限在一個(gè)函數(shù)內(nèi)部的標(biāo)識(shí)符都具有塊作用域,包括在函數(shù)內(nèi)部定義的變量或?qū)ο?、函?shù)的形式參數(shù)等。

(5)作用域限定符::

在函數(shù)中,若局部變量和某個(gè)全局變量同名,可用作用域限定符::存取全局變量的值。用法非常簡(jiǎn)單,就是在變量名前面加上作用域限定符::。

例2-20】 塊作用域及作用域限定符的應(yīng)用。

//Eg2-20.cpp
int i;          //L0
int f(){
    int i;        //L1
    i=1;        //修改L1定義的i
    ::i=0;       //修改L0定義的i
    {
        int j=0;     //L2
        static k;     //L3
        i=2;       //修改L1定義的i
        ::i=3;      //修改L0定義的i
    }          //j,k的作用域到此結(jié)束
    j=2;         //錯(cuò)誤,j已無(wú)定義
    return k;       //錯(cuò)誤,k已失去作用域
}

在本例中,最外層的i和函數(shù)名f具有文件作用域,可在整個(gè)文件中應(yīng)用。f()內(nèi)層的i、j、k具有塊作用域,只能在包含它的最近一對(duì)“{ }”內(nèi)有效。

if、switch、for以及while之類的復(fù)合語(yǔ)句也是一種塊語(yǔ)句,在其中(包括在其條件測(cè)試語(yǔ)句中)定義的名字具有塊作用域,其有效范圍是該語(yǔ)句本身。

例2-21】 下面的程序說(shuō)明在if語(yǔ)句中定義的變量的作用域。假設(shè)在if之前沒(méi)有i和p的任何說(shuō)明和定義。

//Eg2-21.cpp
if(int i=5) {         //i作用域自此開(kāi)始
    int p=0;        //p的作用域自此開(kāi)始
}             //p的作用域到此結(jié)束
else {
    i=1;
    p=2;         //錯(cuò)誤,p無(wú)定義
}             //i的作用域到此結(jié)束

下面的例子說(shuō)明switch中定義的變量的作用域。

void f(int i){
    switch ( int j=i) {  //j的作用域開(kāi)始于此
        case 1:
            j=j+1;
        case 2:
        ……
        case 3:
            cout<<j;
    }          //j的作用域到此結(jié)束
    cout<<j<<endl;    //錯(cuò)誤,j已無(wú)定義
}

對(duì)于for和while循環(huán)語(yǔ)句,標(biāo)準(zhǔn)C++規(guī)定在其循環(huán)測(cè)試條件中定義的變量,其作用域也限于循環(huán)本身,即結(jié)束于循環(huán)體結(jié)束的“}”。按此標(biāo)準(zhǔn),下面的程序存在錯(cuò)誤:

void f1(int z){
    for (int i=0;i<z;i++){
        int j=i;
        cout<<i*j<<endl;
    }            //i的作用域到此結(jié)束
    cout<<i<<endl;      //錯(cuò)誤,i已無(wú)定義
}

但在許多C++編譯器中,這段程序能夠正確編譯和運(yùn)行,原因是,在標(biāo)準(zhǔn)C++之前,上面的for循環(huán)是按如下方式處理的:

int i=0;
for (;i<z;i++){
    ……
}

現(xiàn)在的許多編譯器仍按這種方式處理for循環(huán),如Visual C++就是這樣的。

2.12.2 變量類型及生命期

根據(jù)變量的作用域范圍,變量可分為全局變量和局部變量?jī)纱箢?。在函?shù)內(nèi)部定義的變量就是局部變量(包括函數(shù)參數(shù)),它們只能在定義它的函數(shù)中使用;在函數(shù)之外定義的變量(不屬于任何函數(shù))就是全局變量,其有效范圍從其在文件中的定義位置開(kāi)始到文件結(jié)束。

變量的生命期是指變量在內(nèi)存中存在的時(shí)間,生命期與變量所在的內(nèi)存區(qū)域有關(guān)。為了更清楚地理解這個(gè)問(wèn)題,先看看運(yùn)行程序?qū)?nèi)存的應(yīng)用情況。

一個(gè)程序在其運(yùn)行期間,它的程序代碼和數(shù)據(jù)會(huì)被分別存儲(chǔ)在4個(gè)不同的內(nèi)存區(qū)域,如圖2-3所示。

程序代碼區(qū):程序代碼(即程序的各函數(shù)代碼)存放在此區(qū)域中。

全局?jǐn)?shù)據(jù)區(qū):程序的全局?jǐn)?shù)據(jù)(如全局變量)和靜態(tài)數(shù)據(jù)(static)存放在此區(qū)域中。

圖2-3 內(nèi)存區(qū)域

棧區(qū):程序的局部數(shù)據(jù)(在函數(shù)中定義的數(shù)據(jù))存放在此區(qū)域中。

堆區(qū):程序的動(dòng)態(tài)數(shù)據(jù)(new、malloc就在此區(qū)域中分配存儲(chǔ)空間)存放在此區(qū)域中。

全局?jǐn)?shù)據(jù)區(qū)中的數(shù)據(jù)由C++編譯器建立,對(duì)于定義時(shí)沒(méi)有初始化的變量,系統(tǒng)會(huì)自動(dòng)將其初始化為0。這個(gè)區(qū)域中的數(shù)據(jù)一直保存,直到程序結(jié)束時(shí)才由系統(tǒng)負(fù)責(zé)回收。

堆區(qū)的數(shù)據(jù)由程序員管理,程序員可用new或malloc分配其中的存儲(chǔ)單元給指針變量,用完之后,由程序員用delete或free將其歸還系統(tǒng),以便其他程序使用。

在函數(shù)中定義的局部變量(除了static類型的局部變量外,static類型的變量在全局?jǐn)?shù)據(jù)區(qū)中),只有當(dāng)函數(shù)被調(diào)用時(shí),系統(tǒng)才在棧區(qū)中為它們分配存儲(chǔ)空間,并且不會(huì)對(duì)分配的存儲(chǔ)單元做初始化工作。一旦函數(shù)調(diào)用完成,系統(tǒng)就會(huì)回收這些變量在棧區(qū)中的存儲(chǔ)單元。

全局變量和靜態(tài)變量存儲(chǔ)在全局?jǐn)?shù)據(jù)區(qū)中,它們具有較長(zhǎng)的生命期。非靜態(tài)的局部變量存儲(chǔ)在棧區(qū)中,其生命期很短,只在函數(shù)調(diào)用期間有效。

靜態(tài)變量可分為靜態(tài)全部變量和靜態(tài)局部變量,前者的作用域是整個(gè)程序范圍,后者的作用域局限于定義它的語(yǔ)句塊。靜態(tài)局部變量的作用域與普通局部變量的作用域是相同的,但它與全局變量有著同樣長(zhǎng)的生命期,即程序結(jié)束時(shí)它才會(huì)被釋放。普通局部變量的生命期只有函數(shù)調(diào)用期間才存在,函數(shù)調(diào)用完成后就結(jié)束了。

例2-22】 靜態(tài)變量的生存期長(zhǎng)于其作用域的例子。

// Eg2-22.cpp
#include <iostream.h>
static int n;  //n被初始化為0
void f(){
    static int i;  //i被初始化為0
    int j=0;
    i+=2;
    j+=2;
    cout<<"i="<<i<<", ";
    cout<<"j="<<j<<endl;
}
void main(){
    n+=5;
    f();           //輸出i=2,j=2;
    i=2;           //錯(cuò)誤,i雖然為static,但其作用域?yàn)楹瘮?shù)f()內(nèi)部
    f();         //輸出i=4,j=2;
}              //i,n的生命期到此才結(jié)束

第1次調(diào)用函數(shù)f()后,因?yàn)閕為靜態(tài)變量,雖然失去了作用域(這就是i=2錯(cuò)誤的原因),但卻未失去其生存期(即它占據(jù)的內(nèi)存未被系統(tǒng)回收),第2次調(diào)用函數(shù)f()時(shí),將直接在i對(duì)應(yīng)的存儲(chǔ)器中加2,所以結(jié)果是4。而j是普通局部變量,第1次調(diào)用函數(shù)f()后,其作用域和生存期都結(jié)束了,第2次調(diào)用又重新開(kāi)始,所以兩次調(diào)用函數(shù)f(),j的輸出都是2。

2.12.3 變量初始化

變量初始化的基本原則是:如果定義變量時(shí)提供了初始值表達(dá)式,系統(tǒng)就用這個(gè)表達(dá)式的值作為變量的初值;如果定義變量時(shí)沒(méi)有為它提供初值,則全局?jǐn)?shù)據(jù)區(qū)中的變量將被系統(tǒng)自動(dòng)初始化為0,棧和堆中的變量不被初始化。

全局變量、命名空間的變量、靜態(tài)變量會(huì)被保存在全局?jǐn)?shù)據(jù)區(qū)中,所以它們會(huì)被系統(tǒng)自動(dòng)初始化為0;局部變量(也叫自動(dòng)變量)被存儲(chǔ)在棧區(qū)中,動(dòng)態(tài)分配的變量(用malloc和new建立)被存儲(chǔ)在堆區(qū)中,它們都不會(huì)被系統(tǒng)用默認(rèn)值初始化。

例2-23】 全局變量、靜態(tài)變量、局部變量的初始化。

//Eg2-23.cpp
#include <iostream.h>
int n;                 //初始化為0
void f(){
   static int i;   //初始化為0
   int j;    //不被初始化,j值未知
   cout<<"i="<<i<<", ";
   cout<<"j="<<j<<endl;
}
int *p1;               //p1被初始為0
void main(){
   int *p2;              //p2不被初始化,值未知
   int m;               //m不被初始化,值未知
   f();                //輸出i=0,j=?,?表示不確定值
   cout<<"n="<<n<<endl;       //輸出n=0
   cout<<"m="<<m<<endl;    //輸出m=?,?表示不確定值
   if(p1) cout<<"p1="<<p1<<endl; //p1=0,無(wú)輸出
   if(p2) cout<<"p2="<<p2<<endl; //輸出p2=?,?表示不確定地址
}

2.12.4 局部變量與函數(shù)返回地址

弄清楚了局部變量的存儲(chǔ)方式和生命期之后,當(dāng)用指針或引用從函數(shù)中返回一個(gè)地址時(shí)就要小心了,一定不要返回局部變量的指針或引用。

例2-24】 返回引用的函數(shù)。

//Eg2-24.cpp
#include<iostream>
using namespace std;
int &f1(int x){
   int temp=x;
   return temp;
}
void main(){
   int &i=f1(3);
   cout<<i<<endl;
   cout<<i<<endl;
}

雖然在兩條輸出i的語(yǔ)句間沒(méi)有其他語(yǔ)句,但兩次輸出的結(jié)果仍然可能不一致。下面是在Visual C++ 6.0環(huán)境下的輸出結(jié)果:

3
4200045

第2次輸出的4200045只是一個(gè)隨機(jī)值而已,就算是其他值也是可以理解的。原因很簡(jiǎn)單,函數(shù)f1()返回了局部變量的temp的地址,函數(shù)調(diào)用結(jié)束后,這個(gè)地址就無(wú)效了,會(huì)再次把這個(gè)存儲(chǔ)區(qū)域分配給誰(shuí),它會(huì)怎樣改寫(xiě)這個(gè)存儲(chǔ)區(qū)域中的內(nèi)容,這些都不得而知。

同樣,如果函數(shù)的返回類型是指針,也切忌返回局部變量的地址,它會(huì)引發(fā)與本例同樣的錯(cuò)誤。下面的f1()函數(shù)就存在這樣的問(wèn)題。

int *f1(){
   int temp=1;
   return &temp;
}
主站蜘蛛池模板: 怀安县| 法库县| 长宁区| 万宁市| 芜湖县| 新绛县| 富裕县| 察哈| 呼图壁县| 岑巩县| 民和| 炉霍县| 恩平市| 伊金霍洛旗| 苏州市| 抚宁县| 龙陵县| 上虞市| 桃园市| 镇原县| 灵宝市| 彰化县| 北安市| 瓮安县| 新源县| 牙克石市| 莆田市| 屏山县| 富川| 南召县| 修文县| 区。| 峡江县| 安丘市| 株洲县| 芒康县| 伊吾县| 奎屯市| 罗城| 秀山| 渭源县|