【C/C++開發】malloc,calloc和realloc的區別和注意事項
(1)C語言跟記憶體分配方式
<1>從靜態儲存區域分配.
記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個執行期間都存在.例如全域性變數、static變數.
<2>在棧上建立
在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單元自動被釋放.棧記憶體分配運算內置於處理器的指令集中,效率很高,但是分配的記憶體容量有限.
<3>從堆上分配,亦稱動態記憶體分配.
程式在執行的時候用malloc或new申請任意多少的記憶體,程式設計師自己負責在何時用free或delete釋放記憶體.動態記憶體的生存期由使用者決定,使用非常靈活,但問題也最多.
(2)C語言跟記憶體申請相關的函式主要有 alloca、calloc、malloc、free、realloc等.
<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()試圖直接從堆上現存的資料後面的那些位元組中獲得附加的位元組,如果能夠滿足,自然天下太平;如果資料後面的位元組不夠,問題就出來了,那麼就使用堆上第一個有足夠大小的自由塊,現存的資料然後就被拷貝至新的位置,而老塊則放回到堆上.這句話傳遞的一個重要的資訊就是資料可能被移動.
#include <stdio.h>
#include <malloc.h>
int main(int argc, char* argv[])
{
char *p,*q;
p = (char *)malloc(10);
q = p;
p = (char *)realloc(p,10);
printf("p=0x%x/n",p);
printf("q=0x%x/n",q);
return 0;
}
輸出結果:realloc後,記憶體地址不變
p=0x431a70
q=0x431a70
例2:
#include <stdio.h>
#include <malloc.h>
int main(int argc, char* argv[])
{
char *p,*q;
p = (char *)malloc(10);
q = p;
p = (char *)realloc(p,1000);
printf("p=0x%x/n",p);
printf("q=0x%x/n",q);
return 0;
}
輸出結果:realloc後,記憶體地址發生了變化
p=0x351c0
q=0x431a70
在C語言中,良好的程式設計習慣要求一個函式只做一件事,如果一個函式實現了若干功能,可以說基本是一個糟糕的設計。
C語言 realloc() 函式位於 stdlib.h 標頭檔案中,其原型為:
void *realloc(void *ptr, size_t size);
realloc() 會將 ptr 所指向的記憶體塊的大小修改為 size,並將新的記憶體指標返回。
設之前記憶體塊的大小為 n,如果 size < n,那麼擷取的內容不會發生變化,如果 size > n,那麼新分配的記憶體不會被初始化。
如果 ptr = NULL,那麼相當於呼叫 malloc(size);如果 size = 0,那麼相當於呼叫 free(ptr)。
如果 ptr 不為 NULL,那麼他肯定是由之前的記憶體分配函式返回的,例如 malloc()、calloc()或realloc()。
如果 ptr 所指的記憶體塊被移動,那麼會呼叫 free(ptr)。
看吧,一個簡單的 realloc() 卻賦予了好幾個功能,這並不是良好的函式設計。估計也是為了相容性,才容忍這個函式一直在C庫中。雖然在編碼中,realloc() 會提供一定的方便,但是也很容易引發Bug。
下面就舉兩個例子,來說明一下。
1) realloc() 第一種行為引發的Bug
- void *ptr = realloc(ptr, new_size);
- if (!ptr) {
- // 錯誤處理
- }
正確的處理應該是這樣:
- void *new_ptr = realloc(ptr, new_size);
- if (!new_ptr) {
- // 錯誤處理。
- }
- ptr = new_ptr
2) 第三種行為引發的Bug
實際上,malloc(0)是合法的語句,會返還一個合法的指標,且該指標可以通過free去釋放。這就造成了很多人對realloc()的錯誤理解,認為當size為0時,實際上realloc()也會返回一個合法的指標,後面依然需要使用free去釋放該記憶體。
- void *new_ptr = realloc(old_ptr, new_size);
- //其它程式碼
- free(new_ptr);
所以,realloc() 這個設計並不怎麼優良的函式陷阱還是不少的,一不小心就踩雷了,上面只是兩個簡單的小例子,大家在實際使用的時候還應該注意一些其他小問題。
最近在學C語言,在用到realloc函式時除了一些問題,始終找不到問題所在,後來便一步一步除錯,終於找到了問題,由於前面calloc函式使用時將字串的長度設定錯了,導致在使用realloc時原字串末尾'\0'被清除了,導致了一系列的問題,好在終於解決了,現在來總結一下
realloc使用注意事項(這是總結網友們的經驗)
1. realloc失敗的時候,返回NULL
2. realloc失敗的時候,原來的記憶體不改變,也就是不free或不move,(這個地方很容易出錯)
3. 假如原來的記憶體後面還有足夠多剩餘記憶體的話,realloc的記憶體=原來的記憶體+剩餘記憶體,realloc還是返回原來記憶體的地址; 假如原來的記憶體後面沒有足夠多剩餘記憶體的話,realloc將申請新的記憶體,然後把原來的記憶體資料拷貝到新記憶體裡,原來的記憶體將被free掉,realloc返回新記憶體的地址
4. 如果size為0,效果等同於free()
5. 傳遞給realloc的指標必須是先前通過malloc(), calloc(), 或realloc()分配的
MSDN上
Return Value
realloc returns a void pointer to the reallocated (and possibly moved) memory block.
If there is not enough available memory to expand the block to the given size, the original block is left unchanged, and NULL is returned.
If size is zero, then the block pointed to by memblock is freed; the return value is NULL, and memblock is left pointing at a freed block.
The return value points to a storage space that is guaranteed to be suitably aligned for storage of any type of object. To get a pointer to a type other than void, use a type cast on the return value
Remarks
The realloc function changes the size of an allocated memory block. The memblock argument points to the beginning of the memory block. If memblock is NULL, realloc behaves the same way as malloc and allocates a new block of size bytes. If memblock is not NULL, it should be a pointer returned by a previous call to calloc,malloc, or realloc