C語言 聊聊記憶體的管理方式1 棧 堆和資料區

2021-09-26 08:39:39 字數 4703 閱讀 7536

因為在校的原因,c語言學了快兩年,但偶爾沒敲**,一下就忘了,特意弄個c語言的專題,記錄一下c語言中那些耐人尋味的東西。大綱來自朱有鵬老師的c語言核心深度解析,並參考一些經典的c語言書籍,順序完全是根據自己需要來排版,特此鳴謝。

記憶體真的是乙個聊不完的東西,不管是構成其的器件ram,和cpu的關係,還是其管理方式等,是很多人的痛點。首先時刻銘記一句話:計算機中的程式都是在記憶體中執行的(程式包括**和資料),記憶體對計算機的效能影響非常的大,只要計算機在執行中,cpu就會把需要運算的資料調到記憶體中進行運算,當運算完後cpu再將結果傳出來。因此,在c語言中,所有的變數空間都必須從記憶體中開闢出來。

不管由軟體實現的記憶體管理有多複雜,可以肯定的是,程式需要的記憶體都是來自於物理記憶體。但是如今,在作業系統的計算機上,為了實現記憶體的高效利用,作業系統對所有的物理記憶體進行了統一的記憶體管理,所有應用程式表現出來的都是虛擬記憶體

為了使記憶體管理更加合理,作業系統提供了多種機制來管理記憶體。這些機制各有特點,程式根據實際情況來選擇某種方式獲取記憶體(向作業系統處登記這塊記憶體的臨時使用許可權)、使用記憶體和釋放記憶體(向作業系統歸還這塊記憶體的使用權)。

在c語言程式中,存放資料所能使用的記憶體空間大概分為四種情況:棧stack、堆heap、資料區(.data和.bss區)和常量區(.ro.data)

備註:記憶體的管理其實是對程式而言,而程式包括了**和資料。

(1)空間實現自動管理:程式執行的時候,空間會自動分配,結束時會自動**。棧是自動管理的,程式設計師不需要手動干預,方便簡單,棧因此還稱為自動管理區。

(2)能夠反覆使用:棧內存在程式中用的都是同一塊記憶體空間,程式通過自動開闢和自動釋放,會反覆的使用這一塊空間。

(3)使用後是髒記憶體:由於棧反覆使用,每次使用後程式不會去清理內容,因此當下一次該空間被分配時,上一次使用的值還存在,這也是我們區域性變數為什麼使用的時候需要初始化的原因,因為裡面的值是不確定的。

(4)臨時性:函式不能返回棧變數的指標,因為這個空間在函式執行結束之後就會被自動釋放。上面這句是原話,可以理解為函式呼叫的時候其實是在棧中進行的,這個函式執行完,由於執行的位置其實是隨機的,函式每次執行沒有確定的棧變數指標。

#include int *func(void)  // 返回的是乙個int型的指標,return的應該是個位址

void func2(void)

int main(void)

這是我在gcc上執行的結果

&a = 0xbfc49b6c

in func2, &a = 0xbfc49b6c

in func2, &a = 0xbfc49b6c

p = 0xbfc49b6c

*p = 33.

另外,需要加以說明的是,因為作業系統實現給定了棧的大小,如果在函式中無窮盡的分配區域性變數(包括無休止的遞迴函式),都會使得棧記憶體用完,棧溢位,產生記憶體洩漏的事故。

(1)靈活:是一種管理形式的記憶體區域,管理十分靈活。

(2)記憶體量大:堆記憶體空間很大,程序可以按照需要手動去申請,使用完後手動釋放。

(3)程式手動申請和釋放記憶體:手動表明需要寫**去管理,申請:malloc、釋放:free。

(4)髒記憶體:堆記憶體也是反覆使用的,而且使用者用完釋放前不會清除,因此是髒的。

(5)臨時性:堆內存在malloc後和free之前這段期間內可以被訪問,在malloc之前和free之後是不能被訪問的,否則會有不可預計的後果。

#include #include int main(void)

// 使用申請到的內容

*(p + 0) = 1;

*(p + 1) = 2;

printf("*(p+0) = %d\n*(p+1) = %d\n", *(p+0), *(p+1));

// 第四步:釋放

free(p);

return 0;

}

這是我在gcc上執行的結果

*(p+0) = 1

*(p+1) = 2

(1) 這個程式中,如果最後沒有將分配的堆記憶體空間釋放出來的話,這個記憶體空間會被一直占用,只有整個程式終止之後才會釋放。所以對於堆記憶體而言,使用完後用free釋放空間非常的重要,否則也會造成記憶體洩漏(記憶體空間還在,但是被占用,後續無法再使用,名存實亡)。

(2)malloc返回的是 void * 型別的指標:在使用malloc的分配記憶體的時候,我們並不知道這段空間具體是用來存放什麼型別資料的。型別是在後續使用這些空間存放具體型別資料的時候決定的,所以用空指標型別表示不確定,或者認為該位址指向的空間可以存放任何資料型別,由具體存放的資料型別決定。

(3) malloc的返回值:空間申請成功之後返回的是空間首字元的位址,申請失敗則返回null,因此,malloc獲取的記憶體指標使用前一定要先檢驗是否為null!

(4) 手動釋放:malloc申請的記憶體用完之後,需要free手動釋放。釋放後堆管理器就可以把這段記憶體再次分配給別的使用者。

(5)細節1:在呼叫free歸還p所指向的堆記憶體之前,如果p的指向發生了改變,指向其他地方的話,必須通過乙個中間指標變數先記住p指向的堆記憶體,之後free時才能通過這個中間指標變數釋放之前p所指向的堆空間,否則就會造成之前p指向的堆空間無法釋放,從而導致記憶體洩漏

(6) 細節2:

如果申請malloc(0)返回的是null還是乙個有效指標?答案是不確定的,申請0位元組空間本身就是一件無意義的事情,如果這麼做了,得具體看malloc函式庫的實現者對其的定義;

如果在gcc中申請malloc(4)結果是什麼?因為gcc中預設最小使用的是以16b為單位進行空間分配的。如果指定的空間小於16b的話,會按照16位元組進行空間分配。

編譯器在編譯程式的時候,程式會按照一定的結構,被劃分為各個不同的段進行組織,這些段有**段.text、資料段.data、.bss段。

**段.text:存放程式的**部分,程式中各個執行就放在這裡面

資料段.data:又稱資料區、靜態資料區、靜態區,程式中的靜態變數空間就開闢於此,需要注意的是,全域性變數是整個程式的公共財產,而區域性變數只是函式的私有財產。

.bss段:又叫zi(zero initial)段,所有未初始化或初始化為0的靜態變數的空間就開闢於此,這個段會自動將這些未初始化靜態空間初始化為0。

備註:資料段和.bss段本身實際上沒有區別,都是用來存放程式中的靜態變數,只不過.data中存放的是顯式初始化為非0的靜態資料,而.bss中存放的那些顯示初始化為0或者為顯示初始化的靜態資料。

例如:c語言中使用char *p = "linux"定義字串時,字串"linux"實際上是被分配在**段,也就是"linux"字串實際上是乙個常量字串,而不是變數字串。

另外,在c語言中常常會使用const這個關鍵字來定義常量,常量是不能被修改的量,常量實現方法至少有兩種:一是在編譯的時候,將const修飾的變數放在**段中,以達到不能修改的目的,因為**段都是唯讀的,在微控制器開發中,這種情況非常的常見。二是讓編譯器來幫忙實現,如果編譯器在編譯時,檢查到變數被const修飾,當發現程式試圖去修改該變數時,編譯器就會報錯。本質上,const型的常量還是和普通變數一樣,都被放在了資料段(gcc中其實就是這樣實現的)

記憶體管理其實是對程式的管理,包括兩部分,**管理和資料管理,比較需要注重的是資料的管理,**管理沒有多大的變動。

**管理:**段.text(存放程式中**部分)

資料管理:棧stack、堆heap、資料段.data、.bss段、**段(當存放常量的時候)

棧:存放區域性變數、自動記憶體管理、未初始化時為髒記憶體

堆:需要在程式中自己手動申請,malloc申請,free釋放

資料段:存放的是初始化了非0的靜態變數

.bss段:存放的是未初始化或者初始化為0的靜態變數

**段:存放程式中的**(指令)

備註:常量是乙個需要區分對待的東西,根據不同系統的處方式而定,可以存放在**段上,使得編譯前就是不可修改的;也可以存放在資料段,一旦修改,讓編譯器來提示錯誤。

(1) 如果只是在函式內部臨時使用,作用範圍希望在函式內部,則定義區域性變數

(2) 如果變數只是在程式的乙個階段期間有用,非常適合使用堆記憶體空間;如果變數需要在程式執行的整個過程中一直存在的話,適合使用全域性變數(靜態全域性變數)。

原因:堆記憶體和資料段幾乎具有完全相同的屬性,為什麼這麼說呢?舉個例子:如果是定義的區域性變數,那麼就只在定義處的函式塊中有作用,每次呼叫這個函式的時候都會在棧中重新進行分配,如若不初始化,其實值是不確定的,是棧進行的自動管理。但是堆和資料段不一樣,兩者的生命週期不同,堆記憶體從malloc申請開始到free結束,資料段中的靜態變數從程式一開始執行到程式結束,伴隨程式一直存在。相應的這段期間裡,我們可以自己對這段記憶體的資料進行操作,而不是被動的去讓作業系統自動管理。

C 記憶體管理方式

c 中通過new和delete運算子進行動態記憶體管理 如圖 注意 new delete new和delete一定匹配使用 c庫malloc free等來動態管理記憶體,為什麼c 還要定義new delete運算子來動態管理記憶體呢?因為 c語言是面向過程的語言,錯誤返回錯誤碼,c 是物件導向的語言...

C 記憶體管理方式

在c 中,記憶體分為 棧 堆 自由儲存區 全域性 靜態儲存區 常量儲存區。棧,在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束是這些儲存單元自動被釋放。棧記憶體分配運算內置於處理器的指令集中,效率高,分配的記憶體容量有限。堆,就是那些由malloc等分配的記憶體塊,用free來...

IOS 堆和棧的在記憶體中管理方式

c語言記憶體分配 objective c從名字來看就可以知道是一門超c語言,所以了解c語言的記憶體模型對於理解objective c的記憶體管理有很大的幫助。c語言記憶體模型圖如下 從圖中可以看出記憶體被分成了5個區,每個區儲存的內容如下 1.棧區 stack 存放函式的引數值 區域性變數的值等,由...