1. 程式人生 > >c/c++指標詳解(二)----記憶體分配

c/c++指標詳解(二)----記憶體分配

1、記憶體分配的三種方式:

1)、從靜態儲存區分配。資料的記憶體在程式編譯時已經被分配,該記憶體在整個執行期間長期駐留,不會被釋放;程式結束時,由作業系統自動釋放。這類資料包括靜態資料和全域性資料。

2)、從棧空間分配。函式執行過程中,函式中的區域性變數的記憶體,在棧上被分配;當函式呼叫完成後,隨函式的返回空間也被釋放。

3)、從堆空間分配。由開發者動態的申請記憶體,並手動的釋放記憶體。

本文具體介紹動態記憶體分配,C語言中採用malloc、recalloc等函式分配記憶體;c++中使用new操作符申請記憶體。

malloc函式的返回值為void*,呼叫該函式時,需要顯式的型別轉化。返回值表示記憶體空間的首地址。如果該地址為NULL,表示系統沒有滿足的記憶體可供分配。因此在使用該地址之前,必須判斷是否分配成功。例如:

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

if(p != NULL)

{

......//使用該記憶體空間

}

在該記憶體使用完成後,需要開發者手動釋放該記憶體:free(p):

這裡需要注意:1)、呼叫free後,p和所指向的記憶體地址被斷開,但是p的值仍然沒有變化,此時如果呼叫p,將會出現錯誤,這時p就是一個野指標;因此當free(p);之後,應該將p的值賦為NULL,以免p再次調用出現錯誤。2)、雖然記憶體已經分配完成,但是並沒有初始化,直接使用,取到的結果不正確。

4)、當malloc申請的記憶體不滿足使用者使用要求時,就需要重新分配記憶體,這時可以使用realloc函式。

void *realloc(
   void *memblock,
   size_t size 
);
memblock引數表示已經分配的記憶體地址的指標,即就是p;

size引數表示需要分配的位元組數

該函式的返回值為,重新分配的記憶體空間的首地址。

realloc函式,在malloc函式已經分配的記憶體基礎上再次擴充記憶體。realloc函式的返回值型別仍為void*。可以分三種情況來解釋realloc。

情況一:需要分配的記憶體空間小於已經分配的空間。

那麼這時只是從原來已經分配的記憶體空間中,將多餘的空間釋放,保留realloc函式需要的空間大小,將原來空間的的首地址賦值給realloc函式返回(這個地址和p的值是相同的);如果這樣做的,可能會導致資料出錯,因此一般不要縮小原記憶體空間;

情況二:需要分配的記憶體空間等於已經分配的空間。

將原來記憶體空間的首地址賦值給realloc函式返回;

情況三:需要分配的記憶體空間大於已經分配的空間。

1)如果原來的記憶體後,還有足夠的空間,滿足分配要求,那麼直接在原來的記憶體的後面,分配適量的空間,並將原來空間的首地址賦值給realloc函式,返回;

2)如果原來記憶體後,沒有足量的記憶體空間,滿足分配要求,那麼重新選擇一塊足量的空間分配,並將原來已經分配的空間的資料拷貝到新分配的記憶體中,將原指標p指向的空間釋放。將新分配的空間首地址賦值給realloc函式返回,因此對於原來的空間,realloc函式在分配記憶體時,已經釋放了原來的記憶體,不需要再次釋放,否則會出錯。

情況四:當分配的記憶體大小為0

系統是可以返回一個非NULL值,但是這個空間不能被使用。使用將會出錯,效果等同於free(p);

情況五:當原來的指標為NULL

這種情況,的作用就相當於直接呼叫malloc函式一樣;可以這樣理解,NULL說明原空間沒有分配成功,那麼呼叫realloc函式,肯定需要分配一塊新的記憶體空間,這不就相當於直接呼叫了malloc函式。

情況六:如果realloc函式呼叫失敗

那麼原來的空間地址不會被釋放,保留原來的記憶體,該記憶體空間可以正常使用。

綜上,在使用malloc函式時,需要注意幾點:1)呼叫malloc函式後,需要判斷記憶體是否分配成功;2)使用空間之前,需要給空間賦初始值;3)防止記憶體越界訪問;4)釋放記憶體空間之後free(p);,需要將原指標的值賦空(p=null;),以防再次使用產生錯誤,出現野指標;5)realloc函式對於原始的記憶體空間,在分配記憶體時已經做過處理,因此除realloc函式呼叫失敗外,其餘情況不能再次釋放原空間記憶體,否則會出錯。

2、既然malloc和free已經可以分配記憶體了,為什麼還要引入new和delete?

首先需要明白,malloc和free是c語言中的c庫函式,而new和delete是c++中的識別符號;因此這兩組擁有不同的性質;

接著,malloc和free不僅可以使用在C語言中,也可以使用在c++語言中,但是new和delete只是c++中特有的屬性,在C語言中無法使用,因此new和delete有他的侷限性;

再次,new和delete不僅需要分配記憶體空間,而且在面向物件的語言中,對於物件,還需要完成初始化,觸發物件的構造和析構,這些操作是malloc和free無法完成的(筆者就在曾經實現連結串列的過程中,在結構體定義時,將一個數組元素定義為string型別的,在使用malloc申請記憶體後,呼叫過程中無法給該元素賦值。從這個例子中可以看出雖然malloc和new分配的記憶體大小相等,但是具體的記憶體內部工作還是有區別的)。

最後,malloc在分配記憶體時,需要開發者手動的指定元素的大小,並且返回值需要顯式的型別轉化;但是new就不需要指定和轉化。

3、在c++中,既然new和delete可以完成所有分配的工作,為什麼還要保留malloc和free?

這是因為c++編譯器為了相容c編譯,是c中的所有函式可以在c++編譯器上正常使用,因此保留了malloc和free