C语言内存空间动态分配--实现用户版的malloc

#include <stdio.h>
#include <unistd.h>
#include <string.h>

/**
 * C程序设计语言  第8章  动态分配内存实例
 *
 */

// 默认每次分配最小内存 sizeof(HEADER)
#define N_ALLOCATE 1024


// 用来对齐
typedef long Align;


// 联合体  用来构建链表
union header {
    struct {
        // 指向下一个节点
        union header *ptr;
        // 节点大小
        unsigned size;
    } s;


    // 用来内存对齐
    Align x;
};


// 定义节点类型
typedef union header Header;

// 定位链表指针
static Header *freep = NULL;

// 链表初始节点
static Header base;


// 动态分配内存
void *my_malloc(unsigned);

// 向操作系统申请内存
void *my_morecore(int);

// 释放内存  重新加入链表
void my_free(void *);


// 示例
void main() {
    Header *p;
    char *t;

    // 分配30个字节
    p = (Header *)my_malloc(30);

    // 打印链表节点的size
    printf("%d\n", (p-1)->s.size);


    // t指向新分配的内存地址
    t = (char *)p;
    int i = 0;
    for (; i < 29; i++) {
        // 向新分配的地址写入数据
        t[i] = i + '0';
    }

    // 字符结尾
    t[i] = '\0';

    // 打印
    printf("%s\n", t);
    printf("%lu\n", strlen(t));


}


// 动态分配内存
void *my_malloc(unsigned nbytes) {

    Header *p, *prevp;

    unsigned nunits;

    // 字节转换为节点单位长度
    // + sizeof(Header) 额外加上了节点占用的内存
    // - 1 这里减1的目的是当长度刚好是sizeof(Header)整数倍时 最后面的 + 1 配合使用   / 这个运算会舍去小数部分
    nunits = (nbytes + sizeof(Header) - 1) / sizeof(Header) + 1;

    // 前置节点指向链表
    if ((prevp = freep) == NULL) {

        // 初始化链表节点数据  下一个节点指向了自己
        base.s.ptr = freep = prevp = &base;
        base.s.size = 0;
    }


    // 指针p遍历链表  找出空闲内存块并返回  初始时p指向了下一个节点
    for (p = prevp->s.ptr; ;prevp = p, p = p->s.ptr) {
        if (p->s.size >= nunits) {

            // 空闲内容大于所需内存时
            if (p->s.size > nunits) {

                // 当有节点截出所需大小
                p->s.size -= nunits;

                // 向前移动 形成要返回的节点
                p += p->s.size;
                // 初始化节点大小
                p->s.size = nunits;
            } else {

                // 刚好相等时  直接移除当前节点  将前置节点的下一个节点指向当前节点的下一个节点
                prevp->s.ptr = p->s.ptr;
            }

            // 链表重新定位到前置节点
            freep = prevp;

            // 返回分配节点的下一个地址   节点本身占用内存空间
            return (void *)(p + 1);
        }



        // 遍历链表 回到了初始位置  没有找到合适内存
        if ((p == freep)) {

            // 向系统申请内存
            if ((p = my_morecore(nunits)) == NULL) {
                return NULL;
            }
        }
    }
}


// 向系统申请内存
void *my_morecore(int nunits) {

    char *p;
    Header *t;

    // 每次申请最小单位
    if (nunits < N_ALLOCATE) {
        nunits = N_ALLOCATE;
    }

    p = sbrk(nunits * sizeof(Header));
    // 是否申请成功
    if (p == (char *)-1) {
        return NULL;
    }


    // 申请成功  初始化节点大小
    t = (Header *)p;
    t->s.size = nunits;


    // 节点数据加入空间内存链表
    my_free((void *)(t + 1));


    // 重新返回当前节点
    return freep;
}


// 节点加入空闲链表   并合并地址相邻的内存
void my_free(void *ap) {
    Header *bp, *p;

    // 重新指向节点最开始位置
    bp = (Header *)ap - 1;


    // 遍历链表  找到节点地址的前一个合适节点  主要根据内存地址的顺序

    // 如果在两个节点之前 则退出
    for (p = freep;!(bp > p && bp < p->s.ptr);p = p->s.ptr) {

        // 如果在链表的尾部
        if (p >= p->s.ptr && (bp < p->s.ptr || bp > p)) {
            break;
        }
    }

    // 合并前置节点
    if (bp == (p + p->s.size)) {
        p->s.size = bp->s.size;
    } else {
        p->s.ptr = bp;
    }


    // 合并后置节点
    if ((bp + bp->s.size) == p->s.ptr) {
        bp->s.size += p->s.ptr->s.size;
        bp->s.ptr = p->s.ptr->s.ptr;
    } else {
        bp->s.ptr = p->s.ptr;
    }

    // 链表定位到新加入节点的前一个节点
    freep = p;
}

 

上一篇:2.33


下一篇:new和malloc的区别