一、內存的簡要了解
說到內存,很多人應該都多多少少有點了解了,我們在這再稍微多說幾句:
一般我們可以把內存理解為三個部分:靜態區,棧,堆。有些朋友搞不清到底什么是棧什么是堆,堆棧有多人會認為是堆和棧,兩個放在一塊。其實不然,其中我們口中講的堆棧就是棧,而不是堆。堆的英文是heap ;棧的英文是stack(也翻譯為堆棧)。
存儲內容:
靜態區:保存自動全局變量和static變量(包括static全局和局部變量)。靜態區的內容在整個程序的生命周期內都存在,有編譯器在編譯的時候分配(數據段(存儲全局數據和靜態數據)和代碼段(可執行的代碼/只讀常量))。
棧:保存局部變量。棧上的內容只在函數范圍內存在,當函數運行結束的時候,這些內容也會自動銷毀。其特點是效率高但是空間大小有限。
堆:由malloc系列函數或者new操作符分配的內存。其生命周期由free和delete決定。在沒有釋放之前一直存在,直到函數結束。其特點是使用靈活,空間比較大,但容易出錯。
二、malloc、calloc、realloc
三者的簡單對比:
malloc 函數原型:(void*)malloc(unsigned size);(字節數)
malloc函數在內存中開辟的是一塊連續的空間,size是所需要空間的長度,開辟的大小為size*參數類型,開辟完之后返回這塊空間的首地址。
calloc 函數原型:void* calloc(size_t numElements, size_t sizeOfElements);(元素的個數, 單個元素的字節數)
和malloc相似,它也是開辟一塊連續的空間,空間的大小為:元素的個數*單個元素的字節數。
realloc 函數原型:void* realloc(void* ptr, unsigned newsize);(地址,字節數)
給一個已經分配地址的指針重新分配空間,參數ptr為原有的空間指針,newsize為重新申請的地址長度。它與malloc的區別就是如果你給的指針是NULL,那么你使用的就是malloc,如果你給出的指針是一個已經分配了地址的指針(ptr),那么你使用的就是realloc。
區別:
(1)函數malloc不能初始化所分配的空間,而函數calloc能,也就是說,如果由malloc函數分配的空間原來沒有被分配過,則其中每一位都可能是0;反之,如果這一塊數據塊原來被分配過,那里面可能遺留着各種各樣的數據。所以,當你在使用malloc開辟一塊新空間的時候,要重新初始化那一塊空間(一般調用memset函數來初始化空間)。否則在多次釋放、開辟之后,可能會出現使用錯誤。
(2)calloc函數會將所分配的內存空間中的每一位都初始化為0(這也是它和malloc的主要不同處之一)。也就是說,如果你是為字符類或者整形類的元素分配空間,那么這些元素會保證被初始化為0;如果你是為指針類函數分配內存,那么這些元素都會被初始化為空指針。
(3)malloc向系統申請size個字節的空間,申請完之后返回的是這個空間的首地址,類型為void*,而void*表示未確定的類型,在c/c++中void*可以被強轉成任意類型的指針。
(4)realloc可以對給定的指針所指向的空間進行擴大或者縮小,無論是擴大還是縮小,原有內存中的內容將保持不變(如果對於縮小之后的空間,被縮小的那部分空間內的數據還是會丟失)。realloc並不保證調整后的內存空間和原來的內存空間保持同一個地址。相反,realloc指針很可能指向一個新的地址。
(5)realloc是從堆上分配空間的,但當你進行擴大的時候,realloc會試圖從堆上現存的數據后面的那些字節中獲取附加的字節,如果能滿足,就剛好。但如果后面的字節數不夠,其就會使用堆上第一個有足夠大小的自由塊,然后將現存的數據拷貝到新的位置,將老塊放回到堆上。在這個過程中,數據會被移動。也就是說,當你使用realloc的時候,數據可能被移動。
三、有關malloc的一些擴展(選自《高質量c/c++編程指南(林銳)》)
malloc
malloc的原型:(void*)malloc(int size)(int也可以是unsigned,int只是其中的一種特例)
malloc函數的返回值是一個void類型的指針,參數為int類型數據,即申請分配的內存大小,單位是byte。內存分配成功之后,malloc函數返回這塊內存的首地址。你需要一個指針來接受這個地址。但是由於函數的返回值是void*類型,所以必須強制轉換成你所接收的類型。也就是說,這塊內存將來要存儲什么類型的數據。比如:
char* p = (char*)malloc(100);
在堆上面分配了100個字節內存,返回這塊內存的首地址,把地址強制轉換成char*類型后賦給char*類型 的指針變量p。同時告訴我們這塊內存將來用來存儲char*類型的數據。也就是說你只能通過指針變量p來操作這塊內存。這塊內存本身並沒有名字,對它的訪問是匿名訪問。
內存釋放
有分配就一定有釋放。malloc對應的就是free函數。free函數只有一個參數,就是要釋放的內存塊的首地址。比如:free(p);
free函數做的事情:斬斷指針變量與這塊內存的關系。就像上面的例子一樣malloc函數開辟的這一個數據塊空間是屬於p的,你只能通過p來訪問這一塊數據塊空間,而free函數做的事情就是斬斷malloc和p之間的聯系。但是p指針本身存放的地址並沒有發生變化,只是它對指針所指向的那塊內存已經沒有所有權了,不能對內存塊進行操作。而那塊內存塊里面的數據也沒有被改變,只是你沒有辦法去訪問或者修改那塊數據快中的內容了。
malloc和free是一一對應的,如果malloc兩次但是只free一次就會存在內存泄漏,如果malloc一次但是free了兩次,就會出錯(第一次使用free的時候,malloc所開辟的空間就已經被釋放,第二次使用free就無內存空間可以釋放了,這種對內存的誤操作就有可能會導致程序的崩潰)。
函數的內存釋放完后,一定要把p指針置為NULL。為什么?
從上面可以看出,free掉之后p只是切斷了和內存空間的關系,但是p指針本身內部依舊存在一個地址,如果不把它置成空,那這個指針就會變成一個野指針(懸垂指針),遲早會出事。
1 char* p = (char*)malloc(100); 2 strcpy(p, "hello"); 3 free(p);//可以看到這邊已經釋放了p所指向的那一塊空間,但是p本身存儲的地址並沒有改變 4 if(NULL != p)//判斷不起作用,起不到防護作用 5 { 6 strcpy(p, "world");//p沒有分配空間,出錯。 7 }
四、new/delete
前頭講了很多但好像還沒有講到c++的動態內存這方面。下面我們來進行一些討論。
我們知道c++是兼容c的,那我們明明已經有了malloc和free來進行動態內容的管理,為什么c++還要定義new和delete運算符來動態管理內存。
來看一下它們之間的區別和聯系:
1.它們都是動態管理內存的入口。
2.malloc/free是c/c++標准庫的函數,new/delete是c++操作符。
3.malloc/free只是動態分配/釋放內存空間。而new/delete出來分配空間還會調用構造函數和析構函數進行初始化與清理。
4.malloc/free需要手動計算類型大小且會返回void*, new/delete可以自己計算類型的大小,返回對應類型的指針。
我們在c++中是允許進行重載的,那我們也可以重載一下new和delete,我在這就不做了(其實new和delete是不能重載的,即使你進行了重載,也只是重載了operator new和operator delete)。
有關operator new/operator delete operator new[]/operator delete[]
總結:
1.operator new/operator delete operator new[]/operator delete[]的用法和malloc/free一樣。
2.它們只負責分配空間/釋放空間,不會調用對象構造函數和析構函數來初始化/清理對象。
3.實際operator new/operator delete 只是malloc和free的一層封裝。
五、new和delete在內存中所做的事
