全域性變數和靜態變數以及記憶體堆疊的關係

2021-09-29 22:43:19 字數 3555 閱讀 9288

在編寫程式時,記憶體的控制是很重要的一部分。關於全域性變數和區域性變數,靜態變數的關係如何?以及他們在內存在是儲存於哪部分的?做個記錄,以便往後檢視。

全域性變數:又稱外部變數,與之相對的是區域性變數,是從變數的作用域上來考量的。全域性變數定義與函式外面,作用於整個程式;而區域性變數則是定義在函式裡面,僅作用於函式內部,無法跨函式作用。全域性變數為靜態分配,即程式執行之前就進行記憶體分配,不再改變(記憶體不再改變);而區域性變數則是動態分配,在程式執行的時候進行記憶體分配。

靜態變數:是從變數的記憶體分配上來考量的,與之相對的是動態變數。靜態變數如其名,採用靜態分配方式的變數,表現在**程層面就是用static 或 extern 關鍵字修飾的變數即為靜態變數。靜態變數分為static和extern,兩者作用域不同,static修飾的只能作用於單個程式檔案,而extern 則可以作用於整個程式(多檔案)。

全域性變數和靜態全域性變數:當程式只有乙個檔案構成時,兩個沒有什麼區別;當多個檔案存在時,static全域性變數則使得該變數為原始檔獨享(只作用於單個檔案),而 extern 全域性變數則可以作用整個程式,包括其他原始檔。

那麼靜態區域性變數呢?靜態區域性變數作用域與區域性變數一致,只不過生命週期不一樣,記憶體儲存地點也不一樣。靜態區域性變數是程式執行之前靜態分配記憶體,程式執行結束之後結束。

看到有人提到靜態變數無法改變,這是錯誤的。靜態變數只是記憶體分配為靜態分配方式,記憶體不變,並不是說不可以改變,靜態變數也是可以更改儲存內容的。

我們知道乙個匯程式設計序分為:**段,資料段,堆疊段(這裡的堆疊就是指的棧,stack)。從記憶體上說,資料段又可分為:靜態儲存區,堆(heap)和bss(block started by symbol)區。

**段:就是存放程式二進位制**區域

靜態儲存區(.data):用於存放已初始化的全域性變數和靜態變數

堆(heap):資料區,用於動態分配的儲存。一般由程式設計師控制分配和釋放,malloc 和 new 的資料存放於堆中

未初始化全域性(靜態)變數bss:未初始化的全域性變數和靜態變數,全域性變數和靜態變數預設初始化為0

堆疊(stack):通常理解就是區域性變數,函式呼叫等所需儲存

為了了解全域性變數和靜態變數等記憶體儲存位置,talk is cheap, show the code! 給出乙個測試案例,然後用objdump 或者 readelf反彙編指令,進行檢視該程式生成的對應匯程式設計序結構,獲取各個變數的記憶體段分配。

// tets.c

#include static int var_global_static_a = 1; // 初始化靜態全域性變數

static int var_global_static_b; // 未初始化靜態全域性變數

int var_global_a = 1; // 初始化全域性變數

int var_global_b; // 未初始化全域性變數

int main()

結果如下圖所示:

執行結果可知:已初始化的全域性變數和靜態變數(無論全域性還是區域性)都是存放於.data 段,而未初始化的靜態變數則是存放在.bss 段,未初始化的全域性變數存放在 .comment,這個不知道是幹嘛的,但其實也是應該是 .bss 類似。而區域性變數則並未出現在這裡,因為區域性變數位於堆疊段,是需要程式執行時進行分配的,所以這個只進行了編譯的obj檔案中並沒有這兩個變數的記憶體分配。

.data

var_global_static_a

初始化全域性變數和靜態變數

var_static_a

var_global_a

.bss

var_global_static_b

未初始化全域性變數和靜態變數

var_static_b

var_global_b (顯示是 .com)

未分配var_a

區域性變數

var_b

如果全域性變數初始化都為 0呢?結果如下所示,全部都變成了 .bss 段。其實0是全域性變數和靜態變數的預設初始化值,即如果沒有主動初始化而直接使用,則是預設初始化為0 。所以當主動初始化為0時,還是當成沒有初始化處理,存放於 .bss 段

堆和棧總是一起別人提起,所以這裡也放在一起來講。有個概念需要區分開的,就是堆疊指的就是記憶體的stack部分,而不是堆和棧,堆是heap。有時候定義陣列,因為過大,直接定義成區域性變數的話就會爆,但是用 malloc 或者將陣列變為全域性變數,就不會爆。我們知道這這分別對應著堆疊儲存 和 資料段(堆 和 靜態儲存區),那麼他們的大小分別是多少呢?

堆作為資料段的一部分,理論上除去堆疊大小,**段大小和全域性變數靜態變數等部分,剩下的都可以作為堆的記憶體分配範圍。所以堆的大小一般比較大,幾乎接近計算機記憶體(+虛擬記憶體)大小。關於堆溢位,如果持續進行 malloc 而不釋放的話,那麼就會產生堆溢位,爆堆。

關於堆疊的大小,linux下有個指令可以檢視,一般情況下為8-10m。當區域性變數大小大於這個數值或者遞迴層數過多時,進行發生棧溢位。

ulimit -a

# ulimit -s //可直接檢視堆疊大小

這裡顯示棧大小8m,可以用程式測試一下,棧的大小到底是否是這麼大。測試程式參考 linux下棧空間大小(ulimit):

結果證實了此前查詢得出的棧大小為8m 這個事實(因為還有引數傳遞,和區域性變數,所以是肯定到達不了8的)。為了知道我計算機的實際棧大小,以及後續的證實,進行了手動二分來確定棧具體大小(有點傻。。)

理論上堆疊空間8m=8*1024*1024 = 2*1024*1024 = 2097152 個int

還有 .bss區大小的問題沒有測試,未完待續,有時間再補充吧。。

全域性變數和靜態變數

變數可以分為全域性變數 靜態全域性變數 靜態區域性變數和區域性變數 按儲存區域分 全域性變數 靜態全域性變數和靜態區域性變數都存放在記憶體的全域性資料區,區域性變數存放在記憶體的棧區 按作用域分 全域性變數在整個工程檔案內都有效 靜態全域性變數只在定義它的檔案內有效 靜態區域性變數只在定義它的函式內...

全域性變數和靜態變數

如果定義乙個全域性變數,未被初始化,則變數被預設初始化,但區域性變數未被初始化,則該值未定義。內建型別和陣列一樣 定義全域性靜態變數 在全域性變數前加個關鍵字static,該全域性變數變為全域性靜態變數。全域性靜態變數有以下特點 1 在全域性資料區內分配記憶體 2 如果沒有初始化,其預設值為0 3 ...

全域性變數和靜態變數區別

儲存的地方是一樣的,不同之處在於它們的作用域不同 全域性變數基本上在程式的任何地方都能被看到 而靜態區域性變數只能在其指定的範圍內被使用 比如 int i 全域性變數 class c void main 全域性變數具有外部連線性,即同一工程中其它檔案中的也可引用。而靜態變數不具有外部連線性,即同一工...