程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 內存的連續分配與回收算法

內存的連續分配與回收算法

編輯:C++入門知識

說明:
部分代碼參考《數據結構》書。
1、采用空閒分區鏈鏈接空閒分區,用循環首次適應算法分配內存。
 
2、假定內存塊的大小、地址以“字”為單位計。空閒區、作業區邊界采用標識。
“字”的數據結構如下:
leftLink
tag
size
rightLink
空閒空間
upLink
tag
 
 
3、分配內存時,將符合要求的空閒區的高地址部分分配給作業,以減少修改指針的操作。
 
4、源程序:
[cpp] 
// 空閒分區鏈,邊界標識法 
// 循環首次適應算法 
 
#define _CRT_SECURE_NO_WARNINGS 
#define NDEBUG 
#include <iostream> 
#include <map> 
#include <string> 
#include <cstdio> 
#include <cstdlib> 
#include <cassert> 
using namespace std; 
 
const int MIN_REMAIN  = 3;      // 不保留<=MIN_REMAIN的剩余量 
const int MEMORY_SIZE = 128;    // 內存空間大小(以字計) 
 
enum State{FREE, ALLOC};        // 塊標志:空閒FREE,占用ALLOC 
 
struct Word { 
    union { 
        Word *leftLink;         // 頭部:指向前驅結點 
        Word *upLink;           // 尾部:指向本結點頭部 
    }; 
    State tag;                  // 頭部、尾部:都設塊標志 
    unsigned int size;          // 頭部:塊大小 
    Word *rightLink;            // 頭部:指向後繼結點 
 
    // 默認構造函數 
    Word() 
        :leftLink(NULL),        // 共用體只初始化一個成員即可 
        tag(FREE), 
        size(0), 
        rightLink(NULL){} 
}memory[MEMORY_SIZE];           // 假設內存總量為MEMORY_SIZE個字 
 
struct Job { 
    string name; 
    unsigned int size; 
}; 
 
// 返回front所指向結點的尾部的地址。 
inline Word * FootLoc(Word *front)  // inline? 

    return front + front->size - 1; 

 
// 初始只有一塊空閒區,初始化它的首字和尾部,pav指向首字。 
// 建立雙向循環鏈表,不帶頭結點。 
void Initiate(Word *&pav) 

    pav = memory;   // 表中不設表頭結點,表頭指針pav可以指向表中任一結點 
 
    // 頭部 
    pav->leftLink = memory; 
    pav->rightLink = memory; 
    pav->size = MEMORY_SIZE; 
    pav->tag = FREE; 
 
    // 尾部 
    FootLoc(pav)->upLink = memory; 
    FootLoc(pav)->tag = FREE; 

 
// 若有不小於n的空閒塊,則分配相應的存儲塊,並返回其首地址; 
// 否則返回NULL。 
// 若分配後可利用空間表不空,則pav指向表中剛分配過的結點的後繼結點。 
// n應>=3,包括頭尾兩個字,而實際分配時忽略不計? 
Word * AllocBoundTag (Word *&pav, unsigned int n) 

    if (n <= 2)  { 
        cout << "n<=2,不能分配空間!" << endl; 
        return NULL; 
    } 
    Word *p = NULL; 
    for (p = pav; 
        NULL != p && p->size < n && p->rightLink != pav; 
        p = p->rightLink);           // 查找不小於n的空閒塊 
    if (NULL == p || p->size < n) 
    { 
        cout << "內存可用空間不夠,請先釋放內存。" << endl; 
        return NULL;                // 找不到,返回空指針 
    } 
 
    // p指向找到的空閒塊 
    Word *f = FootLoc(p);           // 指向底部 
    pav = p->rightLink;              // “循環”:pav指向*p結點的後繼結點 
    if (p->size - n <= MIN_REMAIN) {  // 整塊分配,不保留<=MIN_REMAIN的剩余量 
        if (pav == p) { 
            pav = NULL;             // 可利用空間表變為空表 
        } 
        else {                      // 在表中刪除分配的結點 
            pav->leftLink = p->leftLink; 
            p->leftLink->rightLink = pav; 
        } 
        p->tag = ALLOC; 
        f->tag = ALLOC;              // 修改分配結點的頭部和底部標志 
    } 
    else {                          // 分配該塊的後n個字使剩余塊頭部:leftLink, tag, rightLink不變 
        f->tag = ALLOC;              // 分配塊尾部:tag 
        //f->upLink = FootLoc(p) + 1;    // 分配塊尾部:upLink 
        p->size -= n;               // 剩余塊頭部:size 
        // 剩余塊頭部:leftLink, tag, rightLink不變 
        f = FootLoc(p);             // f指向剩余塊尾部 
        f->tag = FREE;               // 剩余塊尾部:tag 
        f->upLink = p;               // 剩余塊尾部:upLink 
        p = f + 1;                  // p指向分配塊頭部 
        p->tag = ALLOC;              // 分配塊頭部:tag 
        p->size = n;                 // 分配塊頭部:size 
        //p->leftLink = NULL;            // 分配塊頭部:leftLink 
        //p->rightLink = NULL;           // 分配塊頭部:rightLink 
    } 
    return p;                       // 返回分配塊首地址 

 
// 1、前後相鄰區都是作業 
void Recycle_AA(Word *&pav, Word *&p) 

    p->tag = FREE;                   // 回收區頭部:tag 
    // 回收區頭部:size 原來就存在 
    FootLoc(p)->upLink = p;          // 回收區尾部:upLink 
    FootLoc(p)->tag = FREE;          // 回收區尾部:tag 
    if (NULL == pav) 
    { 
        pav = p; 
        p->leftLink = p;             // 回收區頭部:leftLink 
        p->rightLink = p;            // 回收區頭部:rightLink 
    }  
    else 
    { 
        // 將p插到pav之前(雙向鏈表的插入操作) 
        p->rightLink = pav;              // 回收區頭部:rightLink 
        p->leftLink = pav->leftLink;      // 回收區頭部:leftLink 
        pav->leftLink->rightLink = p; // pav的前驅頭部:rightLink 
        pav->leftLink = p;               // pav的頭部:leftLink 
 
        pav = p;    // 令剛釋放的結點為下次分配時的最先查詢的結點 
    } 

 
// 2、前空閒,後作業 
void Recycle_FA(Word *&p) 

    unsigned int n = p->size;    // 釋放塊的大小 
    Word *s = (p - 1)->upLink;   // 左鄰空閒區的頭部地址 
    s->size += n;                // 設置新的空閒塊大小 
    Word *f = p + n - 1;            // 設置新的空閒塊底部 
    f->upLink = s; 
    f->tag = FREE; 

 
// 3、前作業,後空閒 
void Recycle_AF(Word *&p) 

    Word *t = p + p->size;       // t指向右鄰空閒區的頭部 
    FootLoc(t)->upLink = p;      // 右鄰空閒區頭部:upLink 
    p->size += t->size;           // 回收區頭部:size 
    p->tag = FREE;               // 回收區頭部:tag 
    p->leftLink = t->leftLink;    // 回收區頭部:leftLink 
    t->leftLink->rightLink = p;   // 原來右鄰區的前驅頭部:rightLink 
    p->rightLink = t->rightLink;// 回收區頭部:rightLink 
    t->rightLink->leftLink = p;   // 原來右鄰區的後繼頭部:leftLink 

 
// 4、前後相鄰區都是空閒區 
void Recycle_FF(Word *&p) 

    unsigned int n = p->size;    // 回收區大小 
    Word *s = (p - 1)->upLink;   // 指向左鄰塊,即新結點頭部 
    Word *t = p + p->size;       // 指向右鄰塊 
    s->size += n + t->size;       // 設置新結點大小 
 
    // 刪除右鄰空閒塊結點(雙向鏈表的刪除操作) 
    t->leftLink->rightLink = t->rightLink; // s不一定等於t->leftLink 
    t->rightLink->leftLink = t->leftLink; 
 
    FootLoc(t)->upLink = s;      // 新結點尾部:upLink指向其頭部 

 
// 釋放首地址為p的作業(頭部中含該作業的大小信息) 
void Recycle(Word *&pav, Word *p) 

    if (!(memory <= p && p < memory + MEMORY_SIZE)) 
    { 
        cout << "釋放區首地址越界!" << endl; 
        return; 
    } 
 
    Word *low = p - 1;          // 與其低地址緊鄰的內存區的底部地址 
    Word *high = p + p->size;    // 與其高地址緊鄰的內存區的頭部地址 
    State lowTag = low->tag; 
    State highTag = high->tag; 
    if (low < memory)            // 當p是內存的第一塊時 
    { 
        lowTag = ALLOC; 
    } 
    if (high >= memory + MEMORY_SIZE)    // 當p是內存的最後一塊時 
    { 
        highTag = ALLOC; 
    } 
 
    // 前後相鄰區都是作業 
    if (ALLOC == lowTag && ALLOC == highTag) 
    { 
        Recycle_AA(pav, p); 
    } 
    // 前空閒,後作業 
    else if (FREE == lowTag && ALLOC == highTag) 
    { 
        Recycle_FA(p); 
    } 
    // 前作業,後空閒 
    else if (ALLOC == lowTag && FREE == highTag) 
    { 
        Recycle_AF(p); 
    } 
    // 前後相鄰區都是空閒區 
    else if (FREE == lowTag && FREE == highTag)  
    { 
        Recycle_FF(p); 
    } 

 
// 輸出內存中各區的信息 
void PrintMInfo(map<Word *, string> &jobAddrToName) 

    cout << "\n************************************" << endl; 
    cout << "起址\t大小\t狀態\t(作業名)" << endl; 
    for (Word *p = memory; p < memory + MEMORY_SIZE; p += p->size) 
    { 
        cout << p - memory << "\t" 
            << p->size << "\t" 
            << p->tag << "\t" 
            << (p->tag == ALLOC ? jobAddrToName[p] : "(空閒)") 
            << endl; 
    } 
    cout << "************************************\n" << endl; 

 
int Flag(const string &option) 

    if (option == "1") return 1; 
    if (option == "2") return 2; 
    if (option == "3") return 3; 
    return 0; 

 
int main(int argc, char **argv) 

    freopen("cin.txt", "r", stdin); 
    map<string, Word *> jobNameToAddr;    // 根據作業名得到它的地址 
    map<Word *, string> jobAddrToName;    // 根據作業地址得到它的名稱 
    Word *pav = NULL;                   // pav為查詢指針 
    Initiate(pav); 
    string option; 
    do 
    { 
        PrintMInfo(jobAddrToName); 
        cout << "請選擇:1、分配內存  2、回收內存  3、退出" << endl; 
        cin >> option; 
        switch (Flag(option)) 
        { 
        case 1: 
            { // 防止error C2374:初始化操作由“case”標簽跳過 
                cout << "請輸入作業名和作業大小:" << endl; 
                Job myJob; 
                cin >> myJob.name >> myJob.size; 
                Word *addr = AllocBoundTag(pav, myJob.size); 
                if (addr == NULL)   // 分配失敗 
                { 
                    break; 
                } 
 
                jobNameToAddr[myJob.name] = addr; 
                jobAddrToName[addr] = myJob.name; 
                break; 
            } 
 
        case 2: 
            { 
                cout << "請輸入要回收的作業名稱:" << endl; 
                string name; 
                cin >> name; 
                Word *addr = jobNameToAddr[name];   // 用戶釋放的內存區的頭部地址為addr 
                if (NULL == addr) 
                { 
                    cout << "作業" << name << "不存在,無法釋放!" << endl; 
                    break; 
                } 
                Recycle(pav, addr); 
 
                int cnt = jobAddrToName.erase(addr); 
                assert(cnt == 1); 
                cnt = jobNameToAddr.erase(name); 
                assert(cnt == 1); 
                break; 
            } 
 
        case 3: 
            return 0; 
 
        default: 
            cout << "輸入錯誤!請重新輸入。" << endl; 
            break; 
        } 
    } while (true); 
 
    return 0; 

 
/* cin.txt
 
1
J5 96
 
1
J2 6
 
1
J4 12
 
1
J3 4
 
1
J1 5
 
1
OS 5
 
2
J4
 
2
J5
 
2
J3
 
1
J6 20
 
3
 
*/ 
 
5、運行結果:
[cpp] view plaincopyprint?
************************************ 
起址    大小    狀態    (作業名) 
0       128     0       (空閒) 
************************************ 
 
請選擇:1、分配內存  2、回收內存  3、退出 
請輸入作業名和作業大小: 
 
************************************ 
起址    大小    狀態    (作業名) 
0       32      0       (空閒) 
32      96      1       J5 
************************************ 
 
請選擇:1、分配內存  2、回收內存  3、退出 
請輸入作業名和作業大小: 
 
************************************ 
起址    大小    狀態    (作業名) 
0       26      0       (空閒) 
26      6       1       J2 
32      96      1       J5 
************************************ 
 
請選擇:1、分配內存  2、回收內存  3、退出 
請輸入作業名和作業大小: 
 
************************************ 
起址    大小    狀態    (作業名) 
0       14      0       (空閒) 
14      12      1       J4 
26      6       1       J2 
32      96      1       J5 
************************************ 
 
請選擇:1、分配內存  2、回收內存  3、退出 
請輸入作業名和作業大小: 
 
************************************ 
起址    大小    狀態    (作業名) 
0       10      0       (空閒) 
10      4       1       J3 
14      12      1       J4 
26      6       1       J2 
32      96      1       J5 
************************************ 
 
請選擇:1、分配內存  2、回收內存  3、退出 
請輸入作業名和作業大小: 
 
************************************ 
起址    大小    狀態    (作業名) 
0       5       0       (空閒) 
5       5       1       J1 
10      4       1       J3 
14      12      1       J4 
26      6       1       J2 
32      96      1       J5 
************************************ 
 
請選擇:1、分配內存  2、回收內存  3、退出 
請輸入作業名和作業大小: 
 
************************************ 
起址    大小    狀態    (作業名) 
0       5       1       OS 
5       5       1       J1 
10      4       1       J3 
14      12      1       J4 
26      6       1       J2 
32      96      1       J5 
************************************ 
 
請選擇:1、分配內存  2、回收內存  3、退出 
請輸入要回收的作業名稱: 
 
************************************ 
起址    大小    狀態    (作業名) 
0       5       1       OS 
5       5       1       J1 
10      4       1       J3 
14      12      0       (空閒) 
26      6       1       J2 
32      96      1       J5 
************************************ 
 
請選擇:1、分配內存  2、回收內存  3、退出 
請輸入要回收的作業名稱: 
 
************************************ 
起址    大小    狀態    (作業名) 
0       5       1       OS 
5       5       1       J1 
10      4       1       J3 
14      12      0       (空閒) 
26      6       1       J2 
32      96      0       (空閒) 
************************************ 
 
請選擇:1、分配內存  2、回收內存  3、退出 
請輸入要回收的作業名稱: 
 
************************************ 
起址    大小    狀態    (作業名) 
0       5       1       OS 
5       5       1       J1 
10      16      0       (空閒) 
26      6       1       J2 
32      96      0       (空閒) 
************************************ 
 
請選擇:1、分配內存  2、回收內存  3、退出 
請輸入作業名和作業大小: 
 
************************************ 
起址    大小    狀態    (作業名) 
0       5       1       OS 
5       5       1       J1 
10      16      0       (空閒) 
26      6       1       J2 
32      76      0       (空閒) 
108     20      1       J6 
************************************ 
 
請選擇:1、分配內存  2、回收內存  3、退出 
請按任意鍵繼續. . . 

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved