C++局部變量、全局變量、靜態變量(堆、棧、靜態存儲區)

1 static關鍵字

1.1 隱藏

eg:

//a.c文件中

char a = 'A';

void msg()

{

     printf("Hello\n");

}

//main.c文件中

extern char a;

printf("%c",a);

輸出結果:A Hello

    所有未加static前綴的全局變量和函數都具有全局可見性,其它的源文件也能訪問。a是全局變量,msg是函數,並且都沒有加static前綴,因此對於另外的源文件main.c是可見的。

    如果加了static,就會對其它源文件隱藏。例如在a和msg的定義前加上static,main.c就看不到它們了。利用這一特性可以在不同的文件中定義同名函數和同名變量,而不必擔心命名衝突。static可以用作函數和變量的前綴,對於函數來講,static的作用僅限於隱藏。

1.2 保持變量內容的持久(static變量中的記憶功能和全局生存期)

eg:

#include <stdio.h>

int fun(){

    static int count = 10; //在第一次進入這個函數的時候,變量a被初始化爲10!並接着自減1,以後每次進入該函數,a

    return count--;    //就不會被再次初始化了,僅進行自減1的操作;在static發明前,要達到同樣的功能,則只能使用全局變量:   

}

int count = 1;

int main(void)

{

     printf("global\t\tlocal static\n");

     for(; count <= 10; ++count)

     printf("%d\t\t%d\n", count, fun());

     return 0;

}

輸出結果:

global  local static

1 10

2 9

3 8

4 7

5 6

6 5

7 4

8 3

9 2

10 1

存儲在靜態數據區的變量會在程序剛開始運行時就完成初始化,也是唯一的一次初始化。共有兩種變量存儲在靜態存儲區:全局變量和static變量,只不過和全局變量比起來,static可以控制變量的可見範圍,說到底static還是用來隱藏的。

---基於以上兩點可以得出一個結論:把局部變量改變爲靜態變量後是改變了它的存儲方式即改變了它的生存期。把全局變量改變爲靜態變量後是改變了它的作用域, 限制了它的使用範圍。因此static 這個說明符在不同的地方所起的作用是不同的。

1.3 默認初始化爲0(static變量)

      其實全局變量也具備這一屬性,因爲全局變量也存儲在靜態數據區。在靜態數據區,內存中所有的字節默認值都是0x00,某些時候這一特點可以減少程序員的工作量。比如初始化一個稀疏矩陣,我們可以一個一個地把所有元素都置0,然後把不是0的幾個元素賦值。如果定義成靜態的,就省去了一開始置0的操作。再比如要把一個字符數組當字符串來用,但又覺得每次在字符數組末尾加‘\0’;太麻煩。如果把字符串定義成靜態的,就省去了這個麻煩,因爲那裏本來就是‘\0’。

1.4 C++中的類成員聲明static(有些地方與以上作用重疊)

         靜態成員函數不含this指針。

         靜態成員是可以獨立訪問的,也就是說,無須創建任何對象實例就可以訪問。

         類的靜態成員函數是屬於整個類而非類的對象,所以它沒有this指針,這就導致 了它僅能訪問類的靜態數據和靜態成員函數。 

 

2 局部變量、全局變量、靜態變量

局部變量:局部變量也只有局部作用域,它是自動對象(auto),它在程序運行期間不是一直存在,而是隻在函數執行期間存在,函數的一次調用執行結束後,變量被撤銷,其所佔用的內存也被收回。

全局變量:全局變量具有全局作用域。全局變量只需在一個源文件中定義,就可以作用於所有的源文件。當然,其他不包含全局變量定義的源文件需要用extern關鍵字再次聲明這個全局變量。

靜態局部變量:靜態局部變量具有局部作用域,它只被初始化一次,自從第一次被初始化直到程序運行結束都一直存在,它和全局變量的區別在於全局變量對所有的函數都是可見的,而靜態局部變量只對定義自己的函數體始終可見。

靜態全局變量:靜態全局變量也具有全局作用域,它與全局變量的區別在於如果程序包含多個文件的話,它作用於定義它的文件裏,不能作用到其它文件裏,即被static關鍵字修飾過的變量具有文件作用域。這樣即使兩個不同的源文件都定義了相同名字的靜態全局變量,它們也是不同的變量。

3 堆

亦稱動態內存分配。程序在運行的時候用malloc或new申請任意大小的內存,程序員自己負責在適當的時候用free或delete釋放內存。動態內存的生存期可以由我們決定,如果我們不釋放內存,程序將在最後才釋放掉動態內存。 但是,良好的編程習慣是:如果某動態內存不再使用,需要將其釋放掉,否則,我們認爲發生了內存泄漏現象。

在C++中需要開闢內存單元的時候

char* str = new char[100];

使用完之後一定要記得釋放內存單元

delete[] str;

str = 0;

4 棧

在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。

int a;

5 靜態存儲區

內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。它主要存放靜態數據、全局數據和常量。

全局變量

static修飾的變量或函數等

下面我們來看看這幾種存儲區的區別:

示例1:靜態存儲區與棧區

char* p = "Hello World1";

char a[] = "Hello World2";

p[2] = 'A';

a[2] = 'A';

char* p1 = "Hello World1";

這個程序是有錯誤的,錯誤發生在p[2] = ‘A'這行代碼處,爲什麼呢,是變量p和變量數組a都存在於棧區的(任何臨時變量都是處於棧區的,包括在main()函數中定義的變量)。但是,數據“Hello World1”和數據“Hello World2”是存儲於不同的區域的。因爲數據“Hello World2”存在於數組中,所以,此數據存儲於棧區,對它修改是沒有任何問題的。因爲指針變量p僅僅能夠存儲某個存儲空間的地址,數據“Hello World1”爲字符串常量,所以存儲在靜態存儲區。雖然通過p[2]可以訪問到靜態存儲區中的第三個數據單元,即字符‘l'所在的存儲的單元。但是因爲數據“Hello World1”爲字符串常量,不可以改變,所以在程序運行時,會報告內存錯誤。並且,如果此時對p和p1輸出的時候會發現p和p1裏面保存的地址是完全相同的。

示例2:棧區和堆區

char* f1()

{

char* p = NULL;

char a;

p = return p;

}

char* f2()

{

char* p = NULL;

p = (char*)new char[4];

return p;

}

這兩個函數都是將某個存儲空間的地址返回,二者有何區別呢?f1()函數雖然返回的是一個存儲空間,但是此空間爲臨時空間。也就是說,此空間只有短暫的生命週期,它的生命週期在函數f1()調用結束時,也就失去了它的生命價值,即:此空間被釋放掉。所以,當調用f1()函數時,如果程序中有下面的語句:

char* p;

p = f1();

*p = 'a';

此時,編譯並不會報告錯誤,但是在程序運行時,會發生異常錯誤。因爲,你對不應該操作的內存(即,已經釋放掉的存儲空間)進行了操作。但是,相比之下,f2()函數不會有任何問題。因爲,new這個命令是在堆中申請存儲空間,一旦申請成功,除非你將其delete或者程序終結,這塊內存將一直存在。也可以這樣理解,堆內存是共享單元,能夠被多個函數共同訪問。如果你需要有多個數據返回卻苦無辦法,堆內存將是一個很好的選擇。

 

總之,對於堆區、棧區和靜態存儲區它們之間最大的不同在於,棧的生命週期很短暫。但是堆區和靜態存儲區的生命週期相當於與程序的生命同時存在(如果您不在程序運行中間將堆內存delete的話),我們將這種變量或數據成爲全局變量或數據。但是,對於堆區的內存空間使用更加靈活,因爲它允許你在不需要它的時候,隨時將它釋放掉,而靜態存儲區將一直存在於程序的整個生命週期中。

原文地址:https://www.cnblogs.com/DannyShi/p/4584750.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章