變數範圍
局部範圍、全域範圍、檔案範圍
局部變數:也稱內部變數,聲明在函數內(包括main函數),範圍開始於變數聲明之處,結束於函數或塊結束處;
全域變數:也稱外部變數,聲明在函數外,範圍一般是整個程式源檔案,範圍最廣,甚至可以作用於組成該程式的所有源檔案(比如,將多個獨立編譯的源檔案連結成一個程式時,在某個源檔案中聲明的全域變數,在與該源檔案相連結的其他源檔案中也可以使用,但使用前必須進行extern全域聲明。這樣,多個源檔案中的所有函數都能使用全域變數,在一個函數中改變了一個全域變數的值,就會作用到使用該全域變數的其他所有函數,相當於各函數之間有了一條直接傳遞資料的通道。當然這也破壞了獨立性)
檔案範圍:在函數外部聲明的變數只在當前檔案範圍內(包括檔案內所有定義的函數)可用,在其他檔案不可用。要使變數具有檔案範圍,必須在變數的聲明前加static關鍵字。當多個源檔案連結成一個程式時,static可以避免一個檔案中的全域變數與其它檔案中的變數同名而發生衝突。
在同一範圍內的變數不能同名,但在不同範圍內的變數可用同名,在本範圍會覆蓋/屏蔽其它範圍的同名變數。(我的地頭我做主)
1. static全域變數和普通全域變數區別。
在全域變數聲明前加static -> static全域變數,全域變數本身是靜態儲存方式,靜態全域變數也是靜態儲存方式,在儲存方式上無區別。
主要區別在於非靜態全域變數的作用於是整個來源程式(由多個來源程式組成的話,在各個源檔案中都有效);靜態全域變數限制了範圍,只在定義該變數的源檔案中有效(全域範圍->檔案範圍),可避免在其他檔案中引起錯誤。static變數只初始化一次,下一次依照上一次的值。
2. static局部變數和普通局部變數區別。
在局部變數聲明前加static -> 靜態局部變數,儲存方式改變了,局部變數是動態儲存裝置,靜態局部變數是靜態儲存方式,也就是改變了它的生存期,由函數內部或者塊內部變為檔案範圍,雖然並不是真正的“檔案範圍”,聲明在函數內,但是在函數外部無法訪問,作用應該只是為了避免重名衝突。
【PS: 靜態局部變數擴大了局部變數的生存期,靜態全域變數縮小了全域變數的範圍】
3. static函數與普通函數區別。
靜態函數與普通函數範圍不同,僅在本檔案。內建函式:只在當前源檔案中聲明和定義的函數稱為內建函式(static);對於可在當前源檔案以外使用的函數,應該在標頭檔中說明,或者進行extern全域聲明。static函數在記憶體中只有一份,普通函數在每個被調用中維持一份拷貝。
函數的儲存類型
分為內建函式(static) 和外部函數(extern)兩類。內建函式:只能被同一個源檔案中的函數所調用;外部函數:可以被其他源檔案中的函數所調用。
沒有定義儲存類型的函數預設extern。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
變數的儲存類型
任何變數都有資料類型和儲存類型兩種屬性。完整的變數聲明,除了聲明代表變數運算屬性的資料類型,有時還需要聲明代表變數儲存方式的儲存類型。
除了分配記憶體空間大小,還要分配不同類型的記憶體空間。
變數的儲存類型決定了C++編譯器為變數分配記憶體的方式,決定了變數的生存期。
準確來說,變數的生存期和範圍是從不同角度分析變數的屬性。
變數的範圍:空間範圍,從代碼空間角度,哪段代碼看得見,哪段代碼看不見。關鍵是“可見度”和“可用性”。
變數的生存期:從代碼的執行時間角度考慮,是指在程式執行的過程中變數從建立到被撤銷的一段時間,即變數的生命週期。當變數被分配了記憶體空間,就處於生存期;當所佔的記憶體空間被釋放,這個變數就結束了生存期。關鍵是“存活狀態”。
最明顯的就是靜態局部變數。
// 靜態局部變數使用
void fun()
{
static int a;//靜態變數a是局部變數,但具有全域變數的生存期
a++;
cout << "a = "<< a<<endl;
}
int main()
{
for(int i = 0; i < 2; i++)//靜態變數a不在範圍內
fun();
}
結果是
a = 1
a = 2
分析:聲明靜態變數時如果沒有初始化,則“初始化”值為0,靜態全域變數的初始化是在執行main之前完畢,靜態局部變數的初始化是在程式運行後第一次執行該變數的聲明語句時進行。
下例:在MyFunc()中第一個聲明的局部變數(x=1)在隨後的語句塊中雖然處於生存期(因為聲明和定義它的MyFunc()函數塊還沒有結束),但不在範圍,因為被語句塊內同名的局部變數x(2)屏蔽(/覆蓋)了,但是一旦語句塊結束,x(2)的生存期也就結束了,在MyFunc()內只有x = 1生存。局部變數x(2),y(2)雖然在語句塊內生存,但進入函數FuncA()後也是停用。
void MyFunc(){
int x = 1;
{
int x(2),y(2);//變數" x = 1"失去範圍
cout << "x = "<<x<<"\n";
FuncA(); //變數X(2),Y(2)進入函數後失去範圍
}
}
記憶體的分配方式:自動分配,靜態分配,動態分配,所佔記憶體地區和變數類型如下:
靜態分配:編譯時間就分配了記憶體位址,運行時變數就佔用記憶體,知道程式結束時變數才釋放記憶體。
動態分配:利用堆(heap)這個記憶體塊為變數分配記憶體,堆使用了除棧、靜態儲存區之外的自由儲存空間。是一種完全由程式自身控制記憶體配置的方式(控制包括開闢空間new、釋放空間delete)
對於非動態記憶體分配方式的變數,決定變數採用哪種記憶體配置方式,是由聲明變數時指定儲存類型和聲明語句在程式中的位置決定的。
C++變數的儲存類:
auto, register, extern, static
聲明時可指定:
<儲存類型> <資料類型> <變數名列表>;
auto int a;
static float b,c;
register和auto聲明局部變數:register儲存在寄存器,auto儲存在棧
extern聲明全域變數,static聲明全域變數和局部變數
extern和static都在靜態儲存區。
沒有指定儲存類型,預設如下:
局部變數:auto
全域變數:extern
全域變數:定義性聲明和引用性聲明
定義性聲明定義變數,分配記憶體空間。只能在函數外部,一般不加extern
引用性聲明表示變數已經在程式源檔案中其他地方進行過定義性聲明(已指派空間)。可以放在函數外,也可以放在函數內,要加extern,一般不能對變數初始化,除非定義性聲明沒有初始化。
程式有多個源檔案組成,如果每個檔案都要使用同一個全域變數,在每個檔案中都要進行全域變數的聲明,但如果是定義性聲明,編譯每個源檔案不會出錯,連結它們則出現重複定義錯誤。所以方法是在一個源檔案中進行定義性聲明,其它檔案引用性聲明。