天天看點

【轉載】變量的存儲類别

 ​

一、動态存儲方式與靜态存儲方式

上一節已介紹了變量的一種屬性——作用域,作用域是從空間的角度來分析的,分為全局變量和局部變量。

變 量還有另一種屬性——存儲期(storage duration,也稱生命期)。存儲期是指變量在記憶體中的存在期間。這是從變量值存在的時間角度來分析的。存儲期可以分為靜态存儲期(static storage duration)和動态存儲期(dynamic storage duration)。這是由變量的靜态存儲方式和動态存儲方式決定的。

所謂靜态存儲方式是指在程式運作期間,系統對變量配置設定固定的存儲空間。而動态存儲方式則是在程式運作期間,系統對變量動态地配置設定存儲空間。

先看一下記憶體中的供使用者使用的存儲空間的情況。這個存儲空間可以分為三部分,即:

(1) 程式區

(2) 靜态存儲區

(3) 動态存儲區

資料分别存放在靜态存儲區和動态存儲區中。全局變量全部存放在靜态存儲區中,在程式開始執行時給全局變量配置設定存儲單元,程式執行完畢就釋放這些空間。在程式執行過程中它們占據固定的存儲單元,而不是動态地進行配置設定和釋放。

在動态存儲區中存放以下資料:

①函數形式參數。在調用函數時給形參配置設定存儲空間。

②函數中的自動變量(未加static聲明的局部變量,詳見後面的介紹)。

③函數調用時的現場保護和傳回位址等。

對以上這些資料,在函數調用開始時配置設定動态存儲空間,函數結束時釋放這些空間。在程式執行過程中,這種配置設定和釋放是動态的,如果在一個程式中兩次調用同一函數,則要進行兩次配置設定和釋放,而兩次配置設定給此函數中局部變量的存儲空間位址可能是不相同的。

如果在一個程式中包含若幹個函數,每個函數中的局部變量的存儲期并不等于整個程式的執行周期,它隻是整個程式執行周期的一部分。根據函數調用的情況,系統對局部變量動态地配置設定和釋放存儲空間。

在 C++中變量除了有資料類型的屬性之外,還有存儲類别(storage class) 的屬性。存儲類别指的是資料在記憶體中存儲的方法。存儲方法分為靜态存儲和動态存儲兩大類。具體包含4種:自動的(auto)、靜态的(static)、寄 存器的(register)和外部的(extern)。根據變量的存儲類别,可以知道變量的作用域和存儲期。

二、自動變量

函 數中的局部變量,如果不用關鍵字static加以聲明,編譯系統對它們是動态地配置設定存儲空間的。函數的形參和在函數中定義的變量(包括在複合語句中定義的 變量)都屬此類。在調用該函數時,系統給形參和函數中定義的變量配置設定存儲空間,資料存儲在動态存儲區中。在函數調用結束時就自動釋放這些空間。如果是在複 合語句中定義的變量,則在變量定義時配置設定存儲空間,在複合語句結束時自動釋放空間。是以這類局部變量稱為自動變量(auto variable)。自動變量用關鍵字auto作存儲類别的聲明。例如:

int f(int a) //定義f函數,a為形參

{

   auto int b,c=3; //定義b和c為整型的自動變量

  ┆

}

存儲類别auto和資料類型int的順序任意。關鍵字auto可以省略,如果不寫auto,則系統把它預設為自動存儲類别,它屬于動态存儲方式。程式中大多

數變量屬于自動變量。本書前面各章所介紹的例子中,在函數中定義的變量都沒有聲明為auto,其實都預設指定為自動變量。在函數體中以下兩種寫法作用相

同:

① auto int b,c=3;

② int b,c=3;

三、用static聲明靜态局部變量

有時希望函數中的局部變量的值在函數調用結束後不消失而保留原值,即其占用的存儲單元不釋放,在下一次該函數調用時,該變量保留上一次函數調用結束時的值。這時就應該指定該局部變量為靜态局部變量(static local variable)。

例4.12 靜态局部變量的值。

#include <iostream>

using namespace std;

int f(int a)  //定義f函數,a為形參

   auto int  b=0; //定義b為自動變量

   static int c=3;//定義c為靜态局部變量

   b=b+1;

   c=c+1;

   return a+b+c;

int main( )

   int a=2,i;

   for(i=0;i<3;i++)

  cout<<f(a)<<″ ″;

  cout<<endl;

  return 0;

運作結果為

7 8 9

先後3次調用f函數時,b和c的值如書中表4.1所示。

對靜态局部變量的說明:

1.靜态局部變量在靜态存儲區内配置設定存儲單元。在程式整個運作期間都不釋放。而自動變量(即動态局部變量)屬于動态存儲類别,存儲在動态存儲區空間(而不是靜态存儲區空間),函數調用結束後即釋放。

2.為靜态局部變量賦初值是在編譯時進行值的,即隻賦初值一次,在程式運作時它已有初值。以後每次調用函數時不再重新賦初值而隻是保留上次函數調用結束時的

值。而為自動變量賦初值,不是在編譯時進行的,而是在函數調用時進行,每調用一次函數重新給一次初值,相當于執行一次指派語句。

3.如果在定義局部變量時不賦初值的話,對靜态局部變量來說,編譯時自動賦初值0(對數值型變量)或空字元(對字元型變量)。而對自動變量來說,如果不賦初值,則它

的值是一個不确定的值。這是由于每次函數調用結束後存儲單元已釋放,下次調用時又重新另配置設定存儲單元,而所配置設定的單元中的值是不确定的。

4.雖然靜态局部變量在函數調用結束後仍然存在,但其他函數是不能引用它的,也就是說,在其他函數中它是“不可見”的。

在什麼情況下需要用局部靜态變量呢?

1.需要保留函數上一次調用結束時的值。例如可以用下例中的方法求n!。

例4.13 輸出1~5的階乘值(即1!,2!,3!,4!,5!)。

int fac(int);  //函數聲明

   int i;

   for(i=1;i<=5;i++)

      cout<<i<<″!=″<<fac(i)<<endl;

   return 0;

int fac(int n)

   static int f=1;  //f為靜态局部變量,函數結束時f的值不釋放

   f=f*n;  //在f原值基礎上乘以n

   return f;

1!=1

2!=2

3!=6

4!=24

5!=120

每次調用fac(i),就輸出一個i,同時保留這個i!的值,以便下次再乘(i+1)。

2.如果初始化後,變量隻被引用而不改變其值,則這時用靜态局部變量比較友善,以免每次調用時重新指派。 但是應該看到,用靜态存儲要多占記憶體,而且降低了程式的可讀性,當調用次數多時往往弄不清靜态局部變量的目前值是什麼。是以,如不必要,不要多用靜态局部變量。

四、用register聲明寄存器變量

一般情況下,變量的值是存放在記憶體中的。當程式中用到哪一個變量的值時,由控制器發出指令将記憶體中該變量的值送到CPU中的運算器。經過運算器進行運算,如果需要存數,再從運算器将資料送到記憶體存放。如圖4.15所示。

為提高執行效率,C++允許将局部變量的值放在CPU中的寄存器中,需要用時直接從寄存器取出參加運算,不必再到記憶體中去存取。這種變量叫做寄存器變量,用關鍵字register作聲明。例如,可以将例4.14中的fac函數改寫如下:

   register int i,f=1; //定義i和f是寄存器變量

   for(i=1;i<=n;i++) f=f*i;

定義f和i是存放在寄存器的局部變量,如果n的值大,則能節約許多執行時間。

在程式中定義寄存器變量對編譯系統隻是建議性(而不是強制性)的。當今的優化編譯系統能夠識别使用頻繁的變量,自動地将這些變量放在寄存器中。

五、用extern聲明外部變量

全局變量(外部變量)是在函數的外部定義的,它的作用域為從變量的定義處開始,到本程式檔案的末尾。在此作用域内,全局變量可以為本檔案中各個函數所引用。編譯時将全局變量配置設定在靜态存儲區。

有時需要用extern來聲明全局變量,以擴充全局變量的作用域。

1. 在一個檔案内聲明全局變量

如果外部變量不在檔案的開頭定義,其有效的作用範圍隻限于定義處到檔案終了。如果在定義點之前的函數想引用該全局變量,則應該在引用之前用關鍵字

extern對該變量作外部變量聲明,表示該變量是一個将在下面定義的全局變量。有了此聲明,就可以從聲明處起,合法地引用該全局變量,這種聲明稱為提前

引用聲明。

例4.14 用extern對外部變量作提前引用聲明,以擴充程式檔案中的作用域。

int max(int,int);  //函數聲明

void main( )

   extern int a,b;//對全局變量a,b作提前引用聲明

   cout<<max(a,b)<<endl;

int a=15,b=-7;//定義全局變量a,b

int max(int x,int y)

   int z;

   z=x>y?x:y;

   return z;

運作結果如下:

15

在main後面定義了全局變量a,b,但由于全局變量定義的位置在函數main之後,是以如果沒有程式的第5行,在main函數中是不能引用全局變量a和b

的。現在我們在main函數第2行用extern對a和b作了提前引用聲明,表示a和b是将在後面定義的變量。這樣在main函數中就可以合法地使用全局

變量a和b了。如果不作extern聲明,編譯時會出錯,系統認為a和b未經定義。一般都把全局變量的定義放在引用它的所有函數之前,這樣可以避免在函數

中多加一個extern聲明。

2. 在多檔案的程式中聲明外部變量

如果一個程式包含兩個檔案,在兩個檔案中都要用到同一個外部變量num,不能分别在兩個檔案中各自定義一個外部變量num。正确的做法是:在任一個檔案中定義外部變量num,而在另一檔案中用extern對num作外部變量聲明。即

   extern int num;

編譯系統由此知道num是一個已在别處定義的外部變量,它先在本檔案中找有無外部變量num,如果有,則将其作用域擴充到本行開始(如上節所述),如果本文

件中無此外部變量,則在程式連接配接時從其他檔案中找有無外部變量num,如果有,則把在另一檔案中定義的外部變量num的作用域擴充到本檔案,在本檔案中可

以合法地引用該外部變量num。

分析下例:

file1.cppfile2.cpp

extern int a,b;  int a=3,b=4;

int main( )  ┆

   cout<<a<<″,″<<b<<endl;

用extern擴充全局變量的作用域,雖然能為程式設計帶來友善,但應十分慎重,因為在執行一個檔案中的函數時,可能會改變了該全局變量的值,進而會影響到另一檔案中的函數執行結果。

六、用static聲明靜态外部變量

有時在程式設計中希望某些外部變量隻限于被本檔案引用,而不能被其他檔案引用。這時可以在定義外部變量時加一個static聲明。例如:

static int a=3;extern int a;

int main ( )  int fun (int n)

{{ ┆

┆a=a*n;

}}

這種加上static聲明、隻能用于本檔案的外部變量(全局變量)稱為靜态外部變量。這就為程式的子產品化、通用性提供了友善。如果已知道其他檔案不需要引用本檔案的全局變量,可以對本檔案中的全局變量都加上static,成為靜态外部變量,以免被其他檔案誤用。

需要指出,不要誤認為用static聲明的外部變量才采用靜态存儲方式(存放在靜态存儲區中),而不加static的是動态存儲(存放在動态存儲區)。實際上,兩種形式的外部變量都用靜态存儲方式,隻是作用範圍不同而已,都是在編譯時配置設定記憶體的。

繼續閱讀