- C++面向?qū)ο蟪绦蛟O(shè)計(jì)
- 杜茂康等編著
- 3152字
- 2019-01-01 05:54:55
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; }
- 平面設(shè)計(jì)初步
- Deep Learning Quick Reference
- Getting Started with Clickteam Fusion
- 水晶石精粹:3ds max & ZBrush三維數(shù)字靜幀藝術(shù)
- 統(tǒng)計(jì)學(xué)習(xí)理論與方法:R語(yǔ)言版
- 高維聚類知識(shí)發(fā)現(xiàn)關(guān)鍵技術(shù)研究及應(yīng)用
- 人工智能趣味入門:光環(huán)板程序設(shè)計(jì)
- Prometheus監(jiān)控實(shí)戰(zhàn)
- 網(wǎng)站前臺(tái)設(shè)計(jì)綜合實(shí)訓(xùn)
- PLC與變頻技術(shù)應(yīng)用
- Learn QGIS
- FANUC工業(yè)機(jī)器人配置與編程技術(shù)
- Microsoft System Center Data Protection Manager Cookbook
- EDA技術(shù)及其創(chuàng)新實(shí)踐(Verilog HDL版)
- 案例解說(shuō)虛擬儀器典型控制應(yīng)用