C程式記憶體分配

2022-07-22 15:03:39 字數 3166 閱讀 9420

從作業系統的角度簡單介紹一下程序。程序是占有資源的最小單位,這個資源當然包括記憶體。在現代作業系統中,每個程序所能訪問的記憶體是互相獨立的(一些交換區除外)。而程序中的執行緒所以共享程序所分配的記憶體空間。

在作業系統的角度來看,程序=程式+資料+pcb(程序控制塊)。

**區(text):用來存放cpu執行的機器指令(machine instructions),也有可能包含一些唯讀的常數變數,例如字串常量等。通常,**區是可共享的(即另外的執行程式可以呼叫它),因為對於頻繁被執行的程式,只需要在記憶體中有乙份**即可。這部分區域的大小在程式執行之前就已經確定,通常是唯讀的,使其唯讀的原因是防止程式意外地修改了它的指令。另外,**區還規劃了區域性變數的相關資訊。

全域性資料區(靜態區)(static):全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域(資料區),未初始化的全域性變數和靜態變數在相鄰的另一塊區域(bss區)。另外文字常量區,常量字串就是放在這裡,程式結束後由系統釋放。

乙個正在執行著的c編譯程式占用的記憶體分為**區、初始化資料區、未初始化資料區、堆區和棧區5個部分。

(1)由編譯器自動分配釋放,通常存放程式臨時建立的區域性變數(但不包括static宣告的變數,static意味著在資料段中存放變數),即函式括大括號 「」 中定義的變數,其中還包括函式呼叫時其形參,呼叫後的返回值等。

(2)呼叫原理:每當乙個函式被呼叫,該函式返回位址和一些關於呼叫的資訊(比如某些暫存器的內容),被儲存到棧區。然後這個被呼叫的函式再為它的自動變數和臨時變數在棧區上分配空間,這就是c實現函式遞迴呼叫的方法。每執行一次遞迴函式呼叫,乙個新的棧框架就會被使用,這樣這個新例項棧裡的變數就不會和該函式的另乙個例項棧裡面的變數混淆

(3)棧是由高位址向低位址擴充套件的資料結構,有先進後出的特點,即依次定義兩個區域性變數,首先定義的變數的位址是高位址,其次變數的位址是低位址。函式引數進棧的順序是從右向左(主要是為了支援可變長引數形式)。

(4)最後棧還具有「小記憶體、自動化、可能會溢位」的特點。棧頂的位址和棧的最大容量一般是系統預先規定好的,通常不會太大。由於棧中主要存放的是區域性變數,而區域性變數的占用的記憶體空間是其所在的**段或函式段結束時由系統**重新利用,所以棧的空間是迴圈利用自動管理的,一般不需要人為操作。如果某次區域性變數申請的空間超過棧的剩餘空間時就有可能出現 「棧的溢位」,進而導致意想不到的後果。所以一般不宜在棧中申請過大的空間,比如長度很大的陣列、遞迴呼叫重複次數很多的函式等等。

(1)通常存放程式執行中動態分配的儲存空間。它的大小,並不固定,可動態擴張或縮放。當程序呼叫malloc/free等函式分配記憶體時,新分配的記憶體就被動態新增到堆上(堆被擴張)/釋放的記憶體從堆中被提出(堆被縮減)。

(2)堆與資料結構中的堆是兩回事,分配方式倒是類似於鍊錶。

(3)堆是低位址向高位址擴充套件的資料結構,是一塊不連續的記憶體區域。在標準c語言上,使用malloc等記憶體分配函式是從堆中分配記憶體的,在objective-c中,使用new建立的物件也是從堆中分配記憶體的。

(4)堆具有「大記憶體、手工分配管理、申請大小隨意、可能會洩露」的特點,堆記憶體是作業系統劃分給堆管理器來管理的,管理器向使用者(使用者程序)提供api(malloc和free等)來使用堆記憶體。需要程式設計師手動分配釋放,如果程式設計師在使用完申請後的堆記憶體卻沒有及時把它釋放掉,那麼這塊記憶體就丟失了(程序自身認為該記憶體沒被使用,但是在堆記憶體記錄中該記憶體仍然屬於這個程序,所以當需要分配空間時又會重新去申請新的記憶體而不是重複利用這塊記憶體),就是我們常說的-記憶體洩漏,所以記憶體洩漏指的是堆記憶體被洩露了。

之所以分成這麼多個區域,主要基於以下考慮:

一般的來說,函式是可以返回區域性變數的。區域性變數的作用域只在函式內部,在函式返回後,區域性變數的記憶體已經釋放了,因此如果函式返回的是區域性變數的值,不涉及位址,程式不會出錯。但是如果返回的是區域性變數的位址(指標)的話,程式執行後會出錯。因為函式只是把指標複製後返回了,但是指標指向的內容已經被釋放了,這樣指標指向的內容就是不可預料的內容,呼叫就會出錯,準確的來說:

下面以函式返回區域性變數的指標舉幾個典型的例子來說明:

1. 返回字串常量指標

#include

char *returnstr()

int main(void)

這個沒有任何問題,因為"hello world!"是乙個字串常量,存放在唯讀資料段,把該字串常量存放的唯讀資料段的首位址賦值給了指標,所以returnstr函式退出時,該該字串常量所在記憶體不會被**,故能夠通過指標順利無誤的訪問。

2. 返回棧指標

#include

char *returnstr()

int main()

"hello world!"是區域性變數存放在棧中,當returnstr函式退出時,棧要清空,區域性變數的記憶體也被清空了,所以這時的函式返回的是乙個已被釋放的記憶體位址,所以有可能列印出來的是亂碼。

3. 返回區域性變數及其指標

可以返回的情況: 區域性變數(僅指變數,不包括指標,無論static還是auto)、靜態區域性變數的指標

不可返回情況: 區域性變數的指標(無static限定詞,預設auto型)

// 返回非靜態區域性變數

int f1()

// 返回靜態區域性變數

int f2()

// 返回非靜態區域性變數指標

int *f3()

// 返回靜態區域性變數指標

int *f4()

區域性變數也分區域性自動變數和區域性靜態變數,由於a返回的是值,因此返回乙個區域性變數是可以的,無論自動還是靜態,因為這時候返回的是這個區域性變數的值,但不應該返回指向區域性自動變數的指標,因為函式呼叫結束後該區域性自動變數被拋棄,這個指標指向乙個不再存在的物件,是無意義的。但可以返回指向區域性靜態變數的指標,因為靜態變數的生存期從定義起到程式結束。

4. 返回靜態棧指標

#include

char *returnstr()

int main(void)

如果函式的返回值非要是乙個區域性變數的位址,那麼該區域性變數一定要申明為static型別

5. 返回陣列指標

沒有使用static限定詞時,陣列預設存放在棧上,所以返回值無意義,如果想返回陣列則需要新增static限定詞,使陣列成為靜態,用法如下

int *func(void)

6. 返回堆指標

程式在執行的時候用 malloc 申請任意多少的記憶體,程式設計師自己負責在何時用 free釋放記憶體,動態記憶體的生存期由程式設計師自己決定,使用非常靈活。

char *getmemory3(int num)

void test3(void)

c 程式記憶體分配

乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 1 棧區 stack 由編譯器自動分配釋放 存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。2 堆區 heap 一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由os 注意它與資料結構中的堆是兩回事,分配方式倒是類...

C程式記憶體分配

在任何程式設計環境及語言中,記憶體管理都十分重要。在目前的計算機系統或嵌入式系統中,記憶體資源仍然是有限的。因此在程式設計中,有效地管理記憶體資源是程式設計師首先考慮的問題。第1節主要介紹記憶體管理基本概念,重點介紹c程式中記憶體的分配,以及c語言編譯後的可執行程式的儲存結構和執行結構,同時還介紹了...

C程式的記憶體分配

c語言有五個區 1.stack 用來存放函式的形參和函式內的區域性變數。由編譯器分配空間,在函式執行完後由編譯器自動釋放。2.heap 用來存放由動態分配函式 如malloc new 分配的空間。是由程式設計師自己手動分配的,並且必須由程式設計師使用free釋放。如果忘記用free釋放,會導致所分配...