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