本文原理地址http://www.cnblogs.com/flywithyou/archive/2011/03/24/flyfly007.html,非常感謝!
一、 HTK內存管理概述
C語言編程中,遇到的關於內存分配和釋放的問題主要有如下兩個方面。
第一是指針維護問題。試想,你寫的一個程序執行了一系列內存空間分配(通常是由malloc函數完成)操作,爲了能夠在以後適當的時候(通常是你不再需要那些內存時)可以將分配的內存空間釋放(通常是由free函數完成),你必須小心謹慎的維護好這些指向分配的內存空間的指針。有經驗的程序員大概都會有這樣的感受,維護這些指針並非易事!特別是當程序比較複雜時,尤爲如此。如果你一不小心(其實這很容易)丟掉了某些指針,那麼操作系統將無法回收那些內存(這便是我們常說的內存泄漏問題),除非你的程序死了。
第二就是關於內存分配釋放操作本身。如果你的程序會相當頻繁的執行malloc和free函數,那麼程序將會費去大量的CPU時間來執行它們。
爲了解決以上兩個問題,儘可能的提高內存利用率,HTK設計了一個內存管理子系統,利用自定義的堆結構(Heap)來進行內存分配和釋放。HTK內存分配和釋放的主要思想是一次向操作系統要大一些的內存塊,然後在將它分成小塊供上層程序使用,不需要時只需釋放那個大內存塊。
HTK把堆結構分爲三大類:
1. M-HEAPS:元素大小固定,new/free操作執行次序無限制,可全局重置(global reset)。
2. M-STACK:元素大小可變,最後分配的空間必須先釋放,可全局重置。最常用
3. C-HEAPS:元素大小可變,new/free操作執行次序無限制,全局重置無效(直接使用malloc和free函數)。最不常用
二、數據結構
1. 堆數據結構定義
typedef enum{MHEAP,MSTAK,CHEAP} HEAPTYPE; // 堆類型定義 typedef unsigned char* ByteP; // 無符號字符(8位)指針 typedef void* Ptr; typedef struct _Block *BlockP; /* MHEAP和MSTAK塊數據結構定義 */ typedef struct _Block{ /* MHEAP ,MSTACK */ size_t numFree; /* 空閒元素數目 ,空閒字節數 */ size_t firstFree; /* 第一個空閒元素索引 ,棧頂索引 */ size_t numElem; /* 塊分配元素的個數 ,塊分配的字節數 */ ByteP used; /* 指向元素分配表指針,1bit/元素 ,不使用 */ Ptr data; /* 指向數據區指針 ,指向數據區指針 */ BlockP next; /* 指向下一個塊指針 ,指向下一個塊指針 */ }Block; /* 堆數據結構定義 */ typedef struct{ /* MHEAP ,MSTACK */ char* name; /* 堆的名稱 ,堆的名稱 */ HEAPTYPE type; /* 堆的類型 ,堆的類型 */ float growf; /* 增長因子 ,增長因子 */ size_t elemSize; /* 元素大小 ,總是1 */ size_t minElem; /**/沒啥作用 size_t maxElem; /* 每個塊最大允許分配的元素個數 ,每個塊最大允許分配的字節數 */ size_t curElem; /* 當前塊元素個數 ,當前塊字節個數 */ size_t totUsed; /* 該堆所有塊已使用的元素總個數 , 該堆所有塊以使用的字節總個數 */ size_t totAlloc; /* 該堆所有塊分配的元素總數 , 該堆所有塊分配的字節總數 */ BlockP heap; /* 指向當前塊的指針 ,指向當前塊的指針 */也是最新的block,因爲最新申請的block都要放在最前面 Boolean protectStk; /* 僅適用於MSTAK */用於實現後進先出原則,見tips }MemHeap;
2. 堆數據結構框圖
M-Heaps內存堆結構示意圖
同一個M-Heaps內存堆中分配的元素大小都是一樣的。堆結構中的塊指針成員變量heap指向數據塊鏈的頭。
數據塊鏈中的每個塊分配的內存區大小由(字節)計算得到。
每個塊中的BYTE型指針成員變量used指向記錄元素使用狀態的表數據結構,表中第i位記錄數據區中第i個元素的使用狀態:1表示使用中、0表示空閒。
每個塊中的firstFree成員變量的值表示數據區中第一個空閒元素的標號。
每個塊中的numFree成員變量的值記錄所在塊中空閒元素的個數。如果numFree爲0表示塊滿,這時firstFree=numElem。
堆與塊示意圖
三、算法
1. 接口描述
TIPS1:如果堆的類型是MHEAP,每次申請內存New只能給你elemSize大小的一塊內存,即使你指定申請大小,也會被忽略,主要用於特定情況的內存申請(比如同樣大小的數據結構),大部分情況下還是使用MSTACK類型的堆,這個堆的elemSize大小爲1,可以申請指定大小的內存。
TIPS2:MSTACK類型,有一個後分配的空間必須先釋放的原則:這個原則有兩個實現方式:第一,通過設置x->protectStk標記爲true。這樣每次在MSTACK堆中申請內存(通過GetElem操作),都會在firstfree下移4bytes處記錄最新申請內存的地址。這樣在dispose某一塊內存比如p時,會檢查當前塊中是否有p,如果有,p是否與firstfree下移4bytes處得值pp相等,如果相等,說明p確實是最新申請的內存, 如果不是,說明是之前申請的內存,根據stack原則,是不能被dispose的;第二,如果沒有設置protectStk,那麼只能靠用戶自己維護這個原則,反正在dispose的時候,是嚴格按照後進先出原則的。A。當前塊必然是最新分配內存的所在塊。B。當前塊內部也是按照最新分配的在最上面原則
TIPS3:使用方法:
1 CreateHeap創建並註冊堆到headlist
2New分配內存,dispose釋放內存
3 ResetHeap 清空堆
4 DeleteHeap刪除並註銷堆從headlist
1. 定義:Export-->void InitMem(void)
說明:初始化全局MSTAK堆變量gstack和全局CHEAP堆變量gcheap。該函數必須在調用任何其它堆操作函數前調用。
參數:無
返回值:無
2. 定義:Export-->void CreateHeap(MemHeap *x , char *name , HeapType type , size_t elemSize , float growf , size_t numElem , size_t maxElem)
說明:創建一個名稱爲name、類型爲type的內存堆,elemSize指定內存堆中元素的大小,numElem指定塊中元素默認個數。如果,內存堆的類型是MSTAK或CHEAP,則elemSize必須爲1。此函數並沒有創建任何真正的內存塊block,New的時候,發現沒有內存或內存不夠的時候才創建block,創建block之後更新x->curElem.這個函數指定的numElem只是初始化x的curElem,和minElem,別的沒啥用。
參數: x: 指向給定的內存堆 [In,Out]
name: 堆的名稱 [In]
type: 堆類型 [In]
elemSize: 對於MHEAP表示堆的每個塊中元素的大小,對於
MSTAK和CHEAP,elemSize必須設爲1 [In]
growf:
numElem: 堆的每個塊默認分配的元素個數 [In]
maxElem: 堆的每個塊最大允許分配的元素個數 [In]
返回值:無
5. 定義:Export-->Ptr New(MemHeap *x , size_t size)
說明:從內存堆x中分配一大小爲size的新元素並返回其指針。如果x類型爲MHEAP則忽略參數size(見tips,如果size大小不等於elemsize,會報錯)。如果分配失敗,程序將會異常退出,所以返回值永遠不會爲NULL。
參數: x: 指向給定的內存堆 [In,Out]
size: 指定分配的元素大小 [In]
返回值:返回指向新分配的元素的指針
7. 定義:void* GetElem(BlockP p , size_t elemSize , HeapType type)
說明:如果type爲MHEAP則從塊p中返回一空閒元素指針,並將其在使用狀態表中的對應項置1。如果type爲MSTAK則從塊p中返回一大小爲elemSize字節數的區域指針,並對塊p中firstFree和numFree變量進行相應的修改。
參數: p: 指向給定的塊 [In]
elemSize: 元素大小 [In]
type: 所屬堆的類型 [In]
返回值:如果成功,則返回大小爲elemSize字節數的數據區,否則返回
NULL。
8. 定義:BlockP AllocBlock(size_t size , size_t num , HeapType type)
說明:分配一個數據區大小爲size*num字節數的塊,在進行必要的初始化後,返回該塊的指針。
參數: size: 元素大小 [In]
num: 元素個數 [In]
type: 所屬堆的類型 [In]
返回值:如果分配成功,則返回塊指針,否則程序異常退出。
9. 定義:size_t Mround(size_t size)
說明:返回大小>=size並且整除FWORD(8)的值。
參數: size 輸入大小 [In]
返回值:返回計算的大小
10. 定義:Export-->Ptr CNew(MemHeap *x , size_t size)
說明:從內存堆x中分配一大小爲size的新元素清0後返回其指針。如果x類型爲MHEAP則忽略參數size。如果分配失敗,程序將會異常退出,所以返回值永遠不會爲NULL。
參數: x: 指向給定的內存堆 [In,Out]
size: 指定分配的元素大小 [In]
返回值: 返回指向新分配的元素的指針
3. 定義:Export-->void ResetHeap(MemHeap *x)
說明:釋放內存堆x中所有元素,對CHEAP內存堆無效。對MHEAP類型,釋放所有block,迴歸x的初始狀態,對MSTACK類型,釋放除第一個block之外的所有block,第一個block的數據區內存也不釋放,但都標記爲未使用(即firstfree=0)
參數: x: 指向給定的內存堆 [In,Out]
返回值:無
4. 定義:Export-->void DeleteHeap(MemHeap *x)
說明:釋放內存堆x中所有元素,對CHEAP內存堆無效。並刪除內存堆x。(MSTACK類型的最後一個block也會釋放),x從headlist裏面去除(UnRecordHeap)
參數: x: 指向給定的內存堆 [In,Out]
返回值:無
11. 定義:Export-->void Dispose(MemHeap* x , void *p)
說明:從內存堆x中釋放元素p;只是標記爲未使用,不釋放。但是如果標記完之後發現此block的內存都未被使用,則釋放該block;
參數: x 指向給定的內存堆 [In,Out]
p 元素指針 [In]
返回值: 無
6. 定義:void BlockRecorder(BlockP *p , int n)
說明:對於MHEAP堆,從塊p向後搜索有n個以上(包括n個)元素的塊,並將其移至塊鏈表頭。對於MSTAK堆,從塊p向後搜索有n個以上(包括n個)字節數的塊,並將其移至塊鏈表頭。
參數: p 指向給定的塊 [In,Out]
n 對於MHEAP,表示元素個數;對於MSTAK,表示字節
數。 [In]
返回值:無
2. 接口實現
1. 內存堆創建算法CreateHeap
void CreateHeap(MemHeap *x, char *name, HeapType type, size_t elemSize,
float growf, size_t numElem, size_t maxElem)
{
// 一致性檢查
if (growf < 0.0) //growf必須大於等於0
HError(5170, "CreateHeap: -ve grow factor in heap %s",name);
if (numElem>maxElem) //默認的元素個數不能大於最大允許的元素個數
HError(5170,"CreateHeap: init num elem > max elem in heap %s",name);
if (elemSize <= 0) //元素大小必須大於0
HError(5170,"CreateHeap: elem size = %u in heap %s",elemSize,name);
if (type == MSTAK && elemSize !=1) //MSTAK的elemSize必須爲1
HError(5170,"CreateHeap: elem size = %u in MSTAK heap %s",elemSize,name);
x->name = (char *)malloc(strlen(name)+1); //爲內存堆名稱分配內存
strcpy(x->name, name);
x->type = type;
x->growf = growf;
x->elemSize = elemSize;
x->maxElem = maxElem;
x->curElem = x->minElem = numElem;
x->totUsed = x->totAlloc = 0;
x->heap = NULL;
x->protectStk = (x==&gstack)? FALSE : protectStaks;
RecordHeap(x); //記錄內存堆x
if (trace&T_TOP)
{
switch (type)
{
case MHEAP: c='M'; break;
case MSTAK: c='S'; break;
case CHEAP: c='C'; break;
}
printf("HMem: Create Heap %s[%c] %u %.1f %u %u\n", name, c,
elemSize, growf, numElem, maxElem);
}
}
1. 內存堆的Trace
爲了跟蹤內存堆的使用情況,HTK使用一個叫MemHeapRec的數據結構來記錄創建的內存堆。MemHeapRec的數據結構如下所示:
typedef struct _MemHeapRec {
MemHeap *heap; // 指向內存堆的指針
struct _MemHeapRec *next; // 指向下一個MemHeapRec
} MemHeapRec;
static MemHeapRec *heapList = NULL; //全局變量, MemHeapRec鏈表
MemHeapRec主要通過RecordHeap和UnRecordHeap兩個函數來完成內存堆的記錄和擦除操作。算法描述如下:
static void RecordHeap(MemHeap *x) //將內存堆x加入到heapList鏈表中
{
MemHeapRec *p;
if (( p = (MemHeapRec *)malloc(sizeof(MemHeapRec))) == NULL)
HError(5105,"RecordHeap: Cannot allocate memory for MemHeapRec");
p->heap = x;
//將p插入到heapList鏈表頭前
p->next = heapList;
heapList = p;
}
static void UnRecordHeap(MemHeap *x) //從heapList中擦除內存堆x記錄
{
MemHeapRec *p, *q;
p = heapList;
q = NULL;
// 從heapList鏈頭向後尋找內存堆x
while (p != NULL && p->heap != x)
{
q = p;
p = p->next;
}
if (p == NULL)
HError(5171,"UnRecordHeap: heap %s not found",x->name); //沒有找到
//將p從heapList中摘除
if (p == heapList)
heapList = p->next;
else
q->next = p->next;
free(p); //釋放p
}
2. 內存堆重置算法ResetHeap
將p從heapList中摘除
void ResetHeap(MemHeap *x)
{
BlockP cur,next;
switch(x->type)
{
case MHEAP:
if (trace&T_TOP)
printf("HMem: ResetHeap %s[M]\n", x->name);
cur = x->heap; //cur指向塊鏈表頭
//刪除所有的塊
while (cur != NULL)
{
next = cur->next;
free(cur->data); //釋放cur塊數據區內存
free(cur->used); //釋放cur塊元素使用狀態表內存
free(cur); //釋放cur塊
cur = next; //cur指向下一個塊
}
x->curElem = x->minElem;
x->totAlloc = 0;
x->heap = NULL;
break;
case MSTAK:
if (trace&T_TOP)
printf("HMem: ResetHeap %s[S]\n", x->name);
cur=x->heap; //cur指向塊鏈表頭
if (cur != NULL)
{
// 刪掉除第一個塊以外的所有塊
while (cur->next != NULL)
{
next = cur->next;
x->totAlloc = x->totAlloc-cur->numElem;
free(cur->data);
free(cur);
cur = next;
}
x->heap = cur;
}
x->curElem = x->minElem;
if (cur != NULL)
{
cur->numFree = cur->numElem;
cur->firstFree = 0;
}
break;
case CHEAP:
HError(5172,"ResetHeap: cannot reset C heap");
}
x->totUsed = 0;
}
3. 內存堆刪除算法DeleteHeap
void DeleteHeap(MemHeap *x) //刪除指定的內存堆x
{
if (x->type == CHEAP)
HError(5172,"DeleteHeap: cant delete C Heap %s",x->name);
ResetHeap(x); //釋放所有的數據塊
if (x->heap != NULL)
{
free(x->heap->data);
free(x->heap);
}
if (trace&T_TOP)
printf("HMem: DeleteHeap %s\n", x->name);
UnRecordHeap(x); //擦除內存堆x的Trace
free(x->name); //釋放分配的名稱內存
}
4. 從內存堆分配空間的算法New、CNew
static BlockP AllocBlock(size_t size, size_t num, HeapType type) //分配塊
{
BlockP p;
ByteP c;
int i;
if (trace&T_TOP)
printf("HMem: AllocBlock of %u bytes\n", num*size);
if ((p = (BlockP) malloc(sizeof(Block))) == NULL) //分配塊空間
HError(5105,"AllocBlock: Cannot allocate Block");
if ((p->data = (void *)malloc(size*num)) == NULL) //分配塊的數據區空間
HError(5105,"AllocBlock: Cannot allocate block data of %u bytes",size*num);
switch (type)
{
case MHEAP:
if ((p->used = (ByteP)malloc((num+7)/8)) == NULL)//分配使用狀態表空間
HError(5105, "AllocBlock: Cannot allocate block used array");
//使用狀態表中所有項賦0
for (i=0,c=p->used; i < (num+7)/8; i++,c++)
*c = 0;
break;
case MSTAK:
p->used = NULL;
break;
default:
HError(5190,"AllocBlock: bad type %d",type);
}
p->numElem = p->numFree = num;
p->firstFree = 0;
p->next = NULL;
return p;
}
AllocBlock分配的MHEAP塊示意圖
//BlockReorder: 從塊p向後尋找第一個有>=n個空閒元素的塊,並將其移至塊鏈表頭
static void BlockReorder(BlockP *p, int n)
{
BlockP head, cur, prev;
if (p == NULL)
return;
head = cur = *p;
prev = NULL;
while (cur != NULL)
{
if (cur->numFree >= n)
{
//找到,那麼就將其移至塊鏈表頭
if (prev != NULL)
{
prev->next = cur->next;
cur->next = head;
}
*p = cur;
return;
}
prev = cur;
cur = cur->next;
}
}
//GetElem: 從塊中分配新元素
static void *GetElem(BlockP p, size_t elemSize, HeapType type)
{
int i,index;
if (p == NULL)
return NULL;
switch (type)
{
case MHEAP:
if (p->numFree == 0)
return NULL;
index = p->firstFree; //第一個空閒元素索引號
p->used[p->firstFree/8] |= 1<<(p->firstFree&7); //使用狀態表中對應位置1
p->numFree--;
//在使用狀態表中尋找下一個空閒塊
if (p->numFree > 0)
{
for (i=p->firstFree+1; i<p->numElem;i++)
{
if ((p->used[i/8] & (1 <<(i&7))) == 0)
{
p->firstFree = i;
break;
}
}
}
else
p->firstFree = p->numElem; //firstFree=最大元素索引號+1
return (void *)((ByteP)p->data+index*elemSize); //返回分配的數據區指針
case MSTAK:
//從棧頂取elemSize字節數的區域
if (p->numFree < elemSize)
return NULL;
index = p->firstFree;
p->firstFree += elemSize;
p->numFree = p->numFree - elemSize;
return (void *)((ByteP)p->data + index); //返回分配的數據區指針
default:
HError(5190,"GetElem: bad type %d", type);
}
return NULL;
}
#define FWORD 8 // size of a full word = 基本的分配量
size_t MRound(size_t size)
{
return ((size % FWORD) == 0) ? size : (size/FWORD + 1) * FWORD;
}
void *New(MemHeap *x, size_t size) //返回從內存堆x分配大小爲size的新元素指針
{
void *q;
BlockP newp;
size_t num,bytes,*ip,chdr;
Boolean noSpace;
Ptr *pp;
//一致性檢查
if (x->elemSize <= 0)//堆X尚未被初始化(沒有createheap,否則elemSize不會小於0)
HError(5174, "New: heap %s not initialised",
(x->name==NULL) ? "Unnamed" : x->name);
switch(x->type)
{
case MHEAP:
//如果能從現有的塊中找到空閒元素,則返回其指針。否則,就分配一個數據
//區大小由curElem,growf以及maxElem決定的新塊。
if (size != 0 && size != x->elemSize) //檢查size的合法性,MHEAP類型,每次只能申請elemSize大小的內存
HError(5173,"New: MHEAP req for %u size elem from heap %s size %u",
size , x->name , x->elemSize);
noSpace = x->totUsed == x->totAlloc; //totUsed和totAlloc分別記錄堆X的所有block的總計已使用內存和已分配內存字節大小,如果totUsed==totAlloc說明x的所有block的內存都已被使用,沒有空閒元素
if (noSpace || (q = GetElem(x->heap , x->elemSize , x->type)) == NULL)
{
if (!noSpace)//如果x還有空間
BlockReorder(&(x->heap), 1); //從塊x->heap 向後尋找第一個有>=n個空閒元素的塊,並將其移至塊鏈表頭,n永遠都得是1
if (noSpace || (q = GetElem(x->heap, x->elemSize, x->type)) == NULL)//如果x沒有空間,則分配一個新塊。現在x->heap應該有空間了??
{
num = (size_t) ((double)x->curElem * (x->growf + 1.0) + 0.5);
if (num > x->maxElem)
num = x->maxElem;
newp = AllocBlock(x->elemSize, num, x->type); //分配新塊
x->totAlloc += num;
x->curElem = num;
//將新分配的塊置於塊鏈表頭
newp->next = x->heap;
x->heap = newp;
if ((q=GetElem(x->heap, x->elemSize, x->type)) == NULL)
HError(5191,"New: null elem but just made block in heap %s",
x->name);
}
}
x->totUsed++;
if (trace&T_MHP)
printf("HMem: %s[M] %u bytes at %p allocated\n", x->name, size, q);
return q;
case CHEAP:
chdr = MRound(sizeof(size_t)); //多分配chdr個字節
q = malloc(size+chdr); //直接使用malloc分配
if (q==NULL)
HError(5105,"New: memory exhausted");
x->totUsed += size;
x->totAlloc += size+chdr;
//在起始部分中記錄分配的空間大小
ip = (size_t *)q;
*ip = size;
if (trace&T_CHP)
printf("HMem: %s[C] %u+%u bytes at %p allocated\n", x->name, chdr,
size, q);
return (Ptr)((ByteP)q+chdr);
case MSTAK:
if (x->protectStk)
size += sizeof(Ptr);
size = MRound(size); //size必須是8的整數倍
if ((q = GetElem(x->heap, size, x->type)) == NULL)
{
//空間不夠,加入一個新塊。這裏爲什麼不像MHEAP那樣查找後面的block有沒有空間?
bytes = (size_t)((double)x->curElem * (x->growf + 1.0) + 0.5);
if (bytes > x->maxElem)
bytes = x->maxElem;
x->curElem = bytes;
if (bytes < size)
bytes = size;
bytes = MRound(bytes);
newp = AllocBlock(1, bytes, x->type);
x->totAlloc += bytes;
//新分配的塊置於塊鏈表頭
newp->next = x->heap;
x->heap = newp;
if ((q=GetElem(x->heap, size, x->type)) == NULL)
HError(5191,"New: null elem but just made block in heap %s",
x->name);
}
x->totUsed += size;
if (trace&T_STK)
printf("HMem: %s[S] %u bytes at %p allocated\n", x->name, size, q);
if (x->protectStk)
{
pp = (Ptr *)((long)q + size - sizeof(Ptr));
*pp = q;
}
return q;
}
return NULL;
}
C-Heaps內存分配示意圖
//返回從內存堆x分配大小爲size的新元素指針,新分配的空間已清0
Ptr CNew (MemHeap *x, size_t size)
{
void *ptr;
ptr = New (x, size);
if (x->type == MHEAP && size ==0)
size = x->elemSize;
memset (ptr, 0, size);
return ptr;
}
5. 從內存堆刪除空間的算法Dispose
void Dispose(MemHeap *x, void *p) //從內存堆x中釋放p
{
BlockP head , cur , prev;
Boolean found = FALSE;
ByteP bp;
size_t size,chdr;
size_t num,index, *ip;
Ptr *pp;
if (x->totUsed == 0)
HError(5105 , "Dispose: heap %s is empty" , x->name);
switch(x->type)
{
case MHEAP:
/*
從塊鏈表頭向後搜索塊,如果data=<p<=data+(numElem-1)*elemSize,表
示p指向的空間是由這個塊的分配的,則計算索引號,使用狀態表中相
應位置0。
*/
head = x->heap;
cur=head;
prev=NULL;
size = x->elemSize;
while (cur != NULL && !found)
{
num = cur->numElem;
found = cur->data <= p &&
(((void*)((ByteP)cur->data+(num-1)*size)) >= p);
if (!found)
{
prev=cur;
cur=cur->next;
}
}
if (cur == NULL)
HError(5175,"Dispose: Item to free in MHEAP %s not found",x->name);
index = ((size_t)p-(size_t)cur->data)/size; //計算索引號
cur->used[index/8] &= ~(1 <<(index&7));
if (index < cur->firstFree)
cur->firstFree = index;
cur->numFree++;
x->totUsed--;
if (cur->numFree == cur->numElem)
{
//釋放整個塊
if (cur != head)
prev->next = cur->next;
else
head = cur->next;
x->heap = head;
x->totAlloc -= cur->numElem;
free(cur->data);
free(cur->used);
free(cur);
}
if (trace&T_MHP)
printf("HMem: %s[M] %u bytes at %p de-allocated\n", x->name, size, p);
return;
case MSTAK:
// 與MHEAP同樣的方法先確定p所在的塊
cur = x->heap;
if (x->protectStk)
{
if (cur->firstFree > 0 ) // s-top in current block
pp = (Ptr *)((size_t)cur->data+cur->firstFree-sizeof(Ptr));
else
{
// s-top in previous block
if (cur->next == NULL)
HError(5175,"Dispose: empty stack");
pp = (Ptr *)((size_t)cur->next->data+cur->next->firstFree-sizeof(Ptr));
}
if (*pp != p)
HError(-5175,"Dispose: violation of stack discipline in %s [%p != %p]",
x->name, *pp, p);
}// if (x->protectStk)
while (cur != NULL && !found)
{
// check current block
num = cur->numElem;
found = cur->data <= p &&
(((void*)((ByteP)cur->data+num)) > p);
if (!found)
{
// p不在當前塊中,所以刪除該塊
x->heap = cur->next;
x->totAlloc -= cur->numElem;
x->totUsed -= cur->firstFree;
free(cur->data);
free(cur);
cur = x->heap;
if (trace&T_STK)
printf("HMem: deleleting block in %s[S]\n", x->name);
}
}
if (!found)
HError(5175,"Dispose: Item to free in MSTAK %s not found", x->name);
size = ((ByteP)cur->data + cur->firstFree) - (ByteP)p; //分配數據區的實際大小
if (size < 0)
HError( 5175 , "Dispose: item to free in MSTAK %s is above stack top",
x->name);
cur->firstFree -= size;
cur->numFree += size;
x->totUsed -= size;
if (trace&T_STK)
printf("HMem: %s[S] %u bytes at %p de-allocated\n", x->name, size, p);
return;
case CHEAP:
chdr = MRound(sizeof(size_t));
bp = (ByteP)p-chdr;
ip = (size_t *)bp;
x->totAlloc -= (*ip + chdr);
x->totUsed -= *ip;
free(bp);
if (trace&T_CHP)
printf("HMem: %s[C] %u+%u bytes at %p de-allocated\n",
x->name, chdr, *ip, bp);
return;
}
}