天天看點

各種alloc傻傻分不清楚

很多新學C語言的童鞋在用到動态記憶體配置設定與使用的時候,對如何選擇各種配置設定函數及其底層差別搞不清楚,那麼下文就認真的講講它們的種種。

(1)C語言的記憶體配置設定方式

<1>從靜态存儲區域配置設定.

    這種方式主要是系統用于自動配置設定給全局變量、static變量記憶體資源的.  它們在程式編譯的時候就已經配置設定好,這塊記憶體在程式的整個運作期間都存在.

<2>在棧上配置設定

      這種方式主要是用于系統自動配置設定給函數内部的局部變量的,函數内局部變量的存儲單元都可以在棧上建立,函數執行結束時這些存儲單元自動被釋放.棧記憶體配置設定運算内置于處理器的指令集中,效率很高,但是配置設定的記憶體容量有限.

<3>從堆上配置設定,亦稱動态記憶體配置設定.

      程式在運作的時候用malloc類或new申請任意多少的記憶體,程式員自己負責在何時用free或delete釋放記憶體.動态記憶體的生存期由使用者決定,使用非常靈活,但問題也最多.

(2)C語言跟記憶體申請相關的函數主要有 alloca、calloc、malloc、realloc、free等.

    <1>alloca是向棧申請記憶體,由系統管理,是以無需釋放.

    <2>malloc配置設定的記憶體是位于堆中的,并且沒有初始化記憶體的内容,是以基本上malloc之後,需要調用函數memset來初始化這部分的記憶體空間.

    <3>calloc則将初始化這部分的記憶體,設定為0.

    <4>realloc則對malloc申請的記憶體進行大小的調整.

    <5>申請的記憶體最終需要通過函數free來釋放.

    當程式運作過程中malloc了,但是沒有free的話,會造成記憶體洩漏.一部分的記憶體沒有被使用,但是由于沒有free,是以系統認為這部分記憶體還在使用,造成不斷的向系統申請記憶體,使得系統可用記憶體不斷減少.但是記憶體洩漏僅僅指程式在運作時,程式退出時,OS将回收所有的資源.是以,适當的重起一下程式,有時候還是有點作用.

【attention】

    三個函數的申明分别是:

        void* malloc(unsigned size);

        void* realloc(void* ptr, unsigned newsize); 

        void* calloc(size_t numElements, size_t sizeOfElement);

    都在stdlib.h函數庫内,它們的傳回值都是請求系統配置設定的位址,如果請求失敗就傳回NULL.

    (1)函數malloc()

        在記憶體的動态存儲區中配置設定一塊長度為size位元組的連續區域,參數size為需要記憶體空間的長度,傳回該區域的首位址.

    (2)函數calloc()

        與malloc相似,參數sizeOfElement為申請位址的機關元素長度,numElements為元素個數,即在記憶體中申請numElements*sizeOfElement位元組大小的連續位址空間.

    (3)函數realloc()

        給一個已經配置設定了位址的指針重新配置設定空間,參數ptr為原有的空間位址,newsize是重新申請的位址長度.

    差別:

    (1)函數malloc不能初始化所配置設定的記憶體空間,而函數calloc能.如果由malloc()函數配置設定的記憶體空間原來沒有被使用過,則其中的每一位可能都是0;反之, 如果這部分記憶體曾經被配置設定過,則其中可能遺留有各種各樣的資料.也就是說,使用malloc()函數的程式開始時(記憶體空間還沒有被重新配置設定)能正常進行,但經過一段時間(記憶體空間還已經被重新配置設定)可能會出現問題.

    (2)函數calloc() 會将所配置設定的記憶體空間中的每一位都初始化為零,也就是說,如果你是為字元類型或整數類型的元素配置設定記憶體,那麼這些元素将保證會被初始化為0;如果你是為指針類型的元素配置設定記憶體,那麼這些元素通常會被初始化為空指針;如果你為實型資料配置設定記憶體,則這些元素會被初始化為浮點型的零.

    (3)函數malloc向系統申請配置設定指定size個位元組的記憶體空間.傳回類型是 void*類型.void*表示未确定類型的指針.C,C++規定,void* 類型可以強制轉換為任何其它類型的指針.

    (4)realloc可以對給定的指針所指的空間進行擴大或者縮小,無論是擴張或是縮小,原有記憶體的中内容将保持不變.當然,對于縮小,則被縮小的那一部分的内容會丢失.realloc并不保證調整後的記憶體空間和原來的記憶體空間保持同一記憶體位址.相反,realloc傳回的指針很可能指向一個新的位址.

    (5)realloc是從堆上配置設定記憶體的.當擴大一塊記憶體空間時,realloc()試圖直接從堆上現存的資料後面的那些位元組中獲得附加的位元組,如果能夠滿足,自然天下太平;如果資料後面的位元組不夠,問題就出來了,那麼就使用堆上第一個有足夠大小的自由塊,現存的資料然後就被拷貝至新的位置,而老塊則放回到堆上.這句話傳遞的一個重要的資訊就是資料可能被移動.

(3)C++關鍵字new和delete用法

  如下幾行代碼:

int *pi=new int;

int *pi=new int();

int *pi=new int(1024);  

  第一行這個new表達式在自由存儲區中配置設定建立了一個整形對象,并傳回一個指向該對象的位址來初始化指針pi。第二行同一行,隻是對指針pi指向的位址的值進行了初始化為0。第三行初始化為1024。

  當動态建立的對象用完後必須釋放記憶體,避免造成記憶體洩漏,可以用delete來完成,new和delete是成對使用的,如下指令釋放pi指向的int型對象所占用的記憶體空間:

delete pi;

  此時pi盡管沒有定義,但仍然存放了它所指向對象的位址,然而pi所指向的記憶體已經被釋放,是以pi不再有效。建議一旦删除指針所指向的對象,立即将指針置為0,這樣就清楚的表明指針不再指向任何對象。 

p=NULL; 

  值得注意的是當執行下清單達式:

int pi=&i;

delete pi;

  編譯器一般不會報錯,因為編譯器通常不能斷定一個指針指向什麼類型的對象,是以盡管這個語句是錯誤的,但在大多數編譯器上仍然能通過。

  C++中允許動态建立const對象:

const int  *pi=new const int(1024);

  動态建立的const對象必須進行初始化,并且進行初始化後的值不能再改變。

  當建立一個動态數組對象和進行記憶體釋放時,執行以下語句:

int *pi=new int[];              //指針pi所指向的數組未初始化

int *pi=new int[n];            //指針pi指向長度為n的數組,未初始化

int *pi=new int[]();            //指針pi所指向的位址初始化為0

delete [] pi;                  //回收pi所指向的數組

(4).new和malloc的差別

a.屬性

  new/delete是C++關鍵字,需要編譯器支援。malloc/free是庫函數,需要頭檔案支援c。

b.參數

  使用new操作符申請記憶體配置設定時無須指定記憶體塊的大小,編譯器會根據類型資訊自行計算。而malloc則需要顯式地指出所需記憶體的尺寸。

c.傳回類型

  new操作符記憶體配置設定成功時,傳回的是對象類型的指針,類型嚴格與對象比對,無須進行類型轉換,故new是符合類型安全性的操作符。而malloc記憶體配置設定成功則是傳回void * ,需要通過強制類型轉換将void*指針轉換成我們需要的類型。

e. 配置設定失敗

  new記憶體配置設定失敗時,會抛出bac_alloc異常。malloc配置設定記憶體失敗時傳回NULL。

f.自定義類型

        new會先調用operator new函數,申請足夠的記憶體(通常底層使用malloc實作)。然後調用類型的構造函數,初始化成員變量,最後傳回自定義類型指針。delete先調用析構函數,然後調用operator delete函數釋放記憶體(通常底層使用free實作)。

        malloc/free是庫函數,隻能動态的申請和釋放記憶體,無法強制要求其做自定義類型對象構造和析構工作。

g.重載

  C++允許重載new/delete操作符,特别的,布局new的就不需要為對象配置設定記憶體,而是指定了一個位址作為記憶體起始區域,new在這段記憶體上為對象調用構造函數完成初始化工作,并傳回此位址。而malloc不允許重載。

h.記憶體區域

  new操作符從自由存儲區(free store)上為對象動态配置設定記憶體空間,而malloc函數從堆上動态配置設定記憶體。自由存儲區是C++基于new操作符的一個抽象概念,凡是通過new操作符進行記憶體申請,該記憶體即為自由存儲區。而堆是作業系統中的術語,是作業系統所維護的一塊特殊記憶體,用于程式的記憶體動态配置設定,C語言使用malloc從堆上配置設定記憶體,使用free釋放已配置設定的對應記憶體。自由存儲區不等于堆,如上所述,布局new就可以不位于堆中。

備注:

1.記憶體空間的初始化函數

memset(void* p, char x, size_t N)

繼續閱讀