1. 程式人生 > >C/C++程式的記憶體分配

C/C++程式的記憶體分配

     轉:https://blog.csdn.net/zcyzsy/article/details/69788884

 

          C/C++編譯的程式的記憶體分配

一、記憶體分配方式:
靜態分配方式:是指在程式編譯和連結的時候分配固定的儲存空間的方式。

動態分配方式:是在程式執行期間根據需要進行動態的分配儲存空間的方式。

靜態記憶體分配是在程式執行之前進行的因而效率比較高,而動態記憶體分配則可以靈活的處理未知數目的。

靜態與動態記憶體分配的主要區別如下:

靜態物件是有名字的變數,可以直接對其進行操作;動態物件是沒有名字的變數,需要通過指標間接地對它進行操作。

靜態物件的分配與釋放由編譯器自動處理;動態物件的分配與釋放必須由程式設計師顯式地管理,它通過malloc,calloc和free等函式(C++中為new和delete運算子)來完成


擴充套件:分配記憶體的函式:(以下函式使用時都是需要包含庫標頭檔案:在C中#include<stdlib.h> / c++ 中 #include<cstdlib>)

      1).分配記憶體空間函式malloc

  呼叫形式: (型別說明符*) malloc (size) 功能:在記憶體的動態儲存區中分配一塊長度為"size" 位元組的連續區域。函式的返回值為該區域的首地址。 “型別說明符”表示把該區域用於何種資料型別。(型別說明符*)表示把返回值強制轉換為該型別指標。“size”是一個無符號數。例如: pc=(char *) malloc (100); 表示分配100個位元組的記憶體空間,並強制轉換為字元陣列型別, 函式的返回值為指向該字元陣列的指標, 把該指標賦予指標變數pc。malloc申請的記憶體位於堆中。呼叫malloc()函式之後,還需呼叫函式memset初始化這部分記憶體空間
 使用方式如下:


int *p1;
p1=(int *)malloc(10*sizeof(int));

  2).分配記憶體空間函式 calloc

  calloc 也用於分配記憶體空間。呼叫形式: (型別說明符*)calloc(n,size)功能:在記憶體動態儲存區中分配n塊長度為“size”位元組的連續區域。函式的返回值為該區域的首地址。(型別說明符*)用於強制型別轉換。calloc函式與malloc 函式的區別僅在於一次可以分配n塊區域。例如: ps=(struet stu*) calloc(2,sizeof (struct stu)); 其中的sizeof(struct stu)是求stu的結構長度。因此該語句的意思是:按stu的長度分配2塊連續區域,強制轉換為stu型別,並把其首地址賦予指標變數ps。動態分配的記憶體進行初始化,全部清零。

使用方式如下:    


ptr=(struct data *)calloc (count,sizeof(strunt data)) //申請並初始化空間


      3).分配記憶體空間函式 alloca

    alloca函式用來在棧中分配size個位元組的記憶體空間,因此函式返回時會自動釋放掉空間。返回值:若分配成功返回指標,失敗則返回NULL。

alloca是向棧申請記憶體,無需釋放,alloca會將這部分記憶體空間初始化為0。

     

       4).分配記憶體空間函式 realloc

     realloc函式用來從堆上分配記憶體,當需要擴大一塊記憶體空間時,realloc()試圖直接從堆上當前記憶體段後面的位元組中獲得更多的記憶體空間,如果能夠滿足,則返回原指標;如果當前記憶體段後面的空閒位元組不夠,那麼就使用堆上第一個能夠滿足這一要求的記憶體塊,將目前的資料複製到新的位置,而將原來的資料塊釋放掉。如果記憶體不足,重新申請空間失敗,則返回NULL。此函式定義如下:

void *realloc(void *ptr,size_t size)
引數ptr為先前由malloc、calloc和realloc所返回的記憶體指標,而引數size為新配置的記憶體大小。

使用如下:


type * ptr;
ptr=realloc(ptr,new_amount);


  5).釋放記憶體空間函式free

  呼叫形式: free(void*ptr); 功能:釋放ptr所指向的一塊記憶體空間,ptr 是一個任意型別的指標變數,它指向被釋放區域的首地址。被釋放區應是由malloc或calloc,alloca函式所分配的區域。
在C語言中,儲存區可以分成全域性區,常量區,程式碼區,棧,堆;如下圖所示(圖片借別人的,有助於理解):


二、記憶體儲存區分配結構:


程式執行時記憶體的結構:

1.全域性區(靜態區)(static):全域性變數和靜態變數的儲存是放在這裡的,初始化的全域性變數和靜態變數在一塊區域,未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域。它是在程式編譯時就已經分配好的,在整個執行期間都存在。程式結束後由系統釋放 。在程式執行過程中它們佔據固定的儲存單元,而不動態地進行分配和釋放。

2. 文字常量區 : 常量儲存,常量字串就是放在這裡的。程式結束後由系統釋放 。
 
3. 程式程式碼區 :存放函式體的二進位制程式碼。

4.堆(heap)區:堆是用於存放程序執行中被動態分配的記憶體段,它的大小並不固定,可動態擴張或縮減,且向上增長。一般由程式設計師手動分配釋放,如果沒手動釋放,在程式結束時可能由OS釋放。當程序呼叫malloc等函式或者new運算子分配記憶體時,新分配的記憶體就被動態新增到堆上(堆被擴張);當利用free等函式或者delete運算子釋放記憶體時,被釋放的記憶體從堆中被剔除(堆被縮減)。(注意:new/delete是運算子,會呼叫構造和解構函式,而malloc/free是庫函式,不會呼叫構造和解構函式。)注意它與資料結構中的堆是兩回事,分配方式類似於連結串列。
  

5.棧(stack)區:棧又稱堆疊,由編譯器自動分配釋放。使用者存放程式臨時建立的區域性變數,也就是說我們函式括弧“{}”中定義的變數(但不包括static宣告的變數,static意味著在全域性區中存放變數)。除此以外,在函式被呼叫時,其引數也會被壓入發起呼叫的程序棧中,並且待到呼叫結束後,函式的返回值也會被存放回棧中。由於棧的先進先出特點,所以棧特別方便用來儲存/恢復呼叫現場。注意棧是向下增長的。使用起來也是和資料結構的棧類似。棧區屬於靜態分配記憶體,預編譯階段就確定好了大小。


補充:

棧和堆的主要區別有以下幾點:

(1)管理方式不同。

  棧編譯器自動管理,無需程式設計師手工控制;而堆空間的申請釋放工作由程式設計師控制,容易產生記憶體洩漏。

(2)空間大小不同。

棧是向低地址擴充套件的資料結構,是一塊連續的記憶體區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,當申請的空間超過棧的剩餘空間時,將提示溢位。因此,使用者能從棧獲得的空間較小。

堆是向高地址擴充套件的資料結構,是不連續的記憶體區域。因為系統是用連結串列來儲存空閒記憶體地址的,且連結串列的遍歷方向是由低地址向高地址。由此可見,堆獲得的空間較靈活,也較大。棧中元素都是一一對應的,不會存在一個記憶體塊從棧中間彈出的情況。

(3)是否產生碎片。

對於堆來講,頻繁的malloc/free(new/delete)勢必會造成記憶體空間的不連續,從而造成大量的碎片,使程式效率降低(雖然程式在退出後作業系統會對記憶體進行回收管理)。對於棧來講,則不會存在這個問題。

(4)增長方向不同。

堆的增長方向是向上的,即向著記憶體地址增加的方向;棧的增長方向是向下的,即向著記憶體地址減小的方向。

(5)分配方式不同。

堆都是程式中由malloc()函式動態申請分配並由free()函式釋放的;棧的分配和釋放是由編譯器完成的,棧的動態分配由alloca()函式完成,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行申請和釋放的,無需手工實現。

(6)分配效率不同。

棧是機器系統提供的資料結構,計算機會在底層對棧提供支援:分配專門的暫存器存放棧的地址,壓棧出棧都有專門的指令執行。堆則是C函式庫提供的,它的機制很複雜,例如為了分配一塊記憶體,庫函式會按照一定的演算法(具體的演算法可以參考資料結構/作業系統)在堆記憶體中搜索可用的足夠大的空間,如果沒有足夠大的空間(可能是由於記憶體碎片太多),就有需要作業系統來重新整理記憶體空間,這樣就有機會分到足夠大小的記憶體,然後返回。顯然,堆的效率比棧要低得多。

程式在儲存時的分割槽:

.bss段:bss段(bss segment)通常是指用來存放程式中未初始化的或者被初始化為0的全域性變數/靜態變數的一塊記憶體區域。BSS是英文BlockStarted by Symbol的簡稱。BSS段屬於靜態記憶體分配。其特點是:可讀寫的,在程式執行之前BSS段會自動清0。所以,未初始的全域性變數在程式執行之前已經成0了。bss並不給該段的資料分配空間,只是記錄資料所需空間的大小.
 
.data段:資料段(data segment)通常是指用來存放程式中已初始化的全域性變數的一塊記憶體區域。資料段屬於靜態記憶體分配。資料段為資料分配空間,資料儲存在目標檔案中。BSS段的大小從可執行檔案中得到,然後連結器得到這個大小的記憶體塊,緊跟在資料段後面。當這個記憶體區進入程式的地址空間後全部清零。包含資料段和BSS段的整個區段此時通常稱為資料區。
 
.text段:程式碼段(codesegment/text segment)通常是指用來存放程式執行程式碼的一塊記憶體區域。這部分割槽域的大小在程式執行前就已經確定,並且記憶體區域通常屬於只讀,某些架構也允許程式碼段為可寫,即允許修改程式。在程式碼段中,也有可能包含一些只讀的常數變數,例如字串常量等。
 
程式碼段屬於程式指令,是可執行語句編譯成機器程式碼後存在這裡,而.bss 和.data段屬於程式資料。程式中資料和指令是分開的。