ISO C說明了三個用于存儲器空間動態分配的函數:
(1)malloc。分配指定字節數的存儲區。此存儲區中的初始值不確定。
(2)calloc。為指定數量具有指定長度的對象分配存儲空間。該空間中的每一位都初始化為0。
(3)realloc。更改以前分配區的長度(增加或減少)。當增加長度時,可能需要將以前分配區的內容移到另一個足夠大的區域,以便在尾端提供增加的存儲區(此時,返回新分配區的指針,否則返回原來的指針值),而新增區域內的初始值則不確定。
#include <stdlib.h>void *malloc( size_t size );void *calloc( size_t nobj, size_t size );void *realloc( void *ptr, size_t newsize );三個函數返回值:若成功則返回非空指針,若出錯則返回NULLvoid free( void *ptr );
這三個分配函數所返回的指針一定是適當對齊的,使其可用于任何數據對象。
因為這三個alloc函數都返回通用指針void *,所以如果在程序中包括了#include <stdlib.h>(以獲得函數原型),那么當我們將這些函數返回的指針賦予一個不同類型的指針時,就不需要顯式地執行類型強制轉換。
函數free釋放ptr指向的存儲空間。被釋放的空間通常被送入可用存儲區池,以后,可在調用上述三個分配函數時再分配。
realloc函數使我們可以增、減以前分配區的長度(最常見的用法是增加該區)。增加該區時,如果在該存儲區后有足夠的空間可供擴充,則可在原存儲區位置上向高地址方向擴充,無需移動任何原先的內容,并返回傳送給它的同樣的指針值。如果在原存儲區后沒有足夠的空間,則realloc分配另一個足夠大的存儲區,將原存儲區中的內容復制到新分配的存儲區。然后,釋放原存儲區,返回新分配區的指針。因為這種存儲區可能會移動位置,所以不應當使任何指針指到該區中。
注意,realloc的最后一個參數是存儲區的newsize(新長度),它不是新、舊存儲區長度之差。作為一個特例,若ptr是一個空指針,則realloc的功能與malloc相同,用于分配一個指定長度為newsize的存儲區。
這些分配例程通常用sbrk(2)系統調用實現。該系統調用擴充(或縮?。┻M程的堆。
雖然sbrk可以擴充或縮小進程的存儲空間,但是大多數malloc和free的實現都不減少進程的存儲空間。釋放的空間可供以后再分配,但通常將它們保持在malloc池中而不返回給內核。
應當注意的是,大多數實現所分配的存儲空間比所要求的要稍大一些,額外的空間用來記錄管理信息——分配塊的長度、指向下一個分配塊的指針等等。這就意味著如果超過一個已分配區的尾端進行寫操作,則會重寫后一個塊的管理記錄。這種類型的錯誤時災難性的,但是因為這種錯誤不會很快暴露出來,所以也就很難發現。同樣,在已分配區起始位置之前進行寫操作會重寫本塊的管理記錄。
在動態分配的緩沖區前或后進行寫操作,破壞的可能不僅僅是該區的管理記錄信息。在動態分配的緩沖區前后的存儲區很可能用于其他動態分配的對象。這些對象與破壞它們的代碼可能無關,這造成尋求信息破壞的源頭更加困難。
其他可能產生的致命性錯誤是:釋放一個已經釋放了的塊;調用free時所用的指針不是三個alloc函數的返回值等。如若一個進程調用malloc函數,但卻忘記調用free函數,那么該進程占用的存儲器就會連續增加,這被稱為泄漏(leakage)。不調用free函數以釋放不再使用的空間,那么進程地址空間長度就會慢慢增加,直至不再有空閑空間。此時,由于過度的分頁開銷,因而使性能下降。
因為存儲器分配出錯很難跟蹤,所以某些系統提供了這些函數的另一種實現版本。每次調用這三個分配函數中的任意一個或free時,它們都進行附加的檢錯。在調用連接編輯器時指定一個專用庫,則在程序中就可使用這種版本的函數。此外還有公共可用的資源,在對其進行編譯時使用一個特殊標志就會使附加的運行時檢查生效。
本篇博文內容摘自《UNIX環境高級編程》(第二版),僅作個人學習記錄所用。關于本書可參考:http://www.apuebook.com/。
新聞熱點
疑難解答