c++中的malloc底层实现代码

C++中的malloc是一种动态内存分配的方式,它可以在程序运行期间动态分配所需的内存大小,以满足程序运行时所需的空间需求。malloc的底层实现需要非常细致的代码编写,以下就介绍其详细实现攻略及示例说明。

1. malloc底层实现攻略

malloc的底层实现需要在内存管理器中调用操作系统API获取更多的内存空间,然后通过算法来管理这些空间的分配和释放。下面是malloc的详细实现攻略:

步骤一:定义管理内存的结构体

首先,需要定义一个结构体,用于管理动态分配的内存。该结构体包括三个主要的成员:已使用的内存大小、空闲的内存大小和一个指向首个空闲内存块的指针。

struct MemoryBlock {
    size_t size; // 已使用的内存大小
    MemoryBlock* nxt; // 指向下一个内存块的指针
};

步骤二:定义内存管理器

其次,需要定义一个内存管理器,用于管理所有动态分配的内存,并提供分配和释放内存的相关函数。这个管理器使用一个循环链表来保存所有空闲的内存块。循环链表的头是一个指向第一个空闲内存块的指针,它永远不为空。如果没有任何可用内存块,则内存管理器将调用操作系统API请求更多内存。

class MemoryManager {
public:
    MemoryManager() {
        _head = nullptr;
        _free_mem_size = 0;
        _used_mem_size = 0;
    }
    ~MemoryManager() {
        ReleaseAllMem();
    }

    // 动态分配内存
    void* Allocate(size_t size);

    // 释放已分配的内存
    void Free(void* p);

private:

    // 释放所有已分配内存的函数
    void ReleaseAllMem();

    MemoryBlock* _head; // 指向第一个空闲内存块的指针
    size_t _free_mem_size; // 空闲内存大小
    size_t _used_mem_size; // 已使用的内存大小
};

步骤三:实现内存分配函数

接下来,需要实现内存分配函数Allocate。该函数按照给定的大小分配内存,并返回一个指向新分配的内存的指针。该函数的实现过程如下:

  1. 检查是否有足够的空闲内存块,如果没有,则调用操作系统API获取更多的内存空间。

  2. 遍历空闲内存块列表,寻找一个空间大于等于请求大小的内存块。

  3. 如果找到了,则从空闲块链表中移除该块,并返回指向该块的指针。

  4. 如果没有找到,则调用操作系统API获取更多内存空间,然后重新开始执行第二步。

void* MemoryManager::Allocate(size_t size) {
    if(size == 0) {
        return nullptr;
    }

    // 遍历空闲内存块链表,寻找一个空闲块
    MemoryBlock* p = _head;
    MemoryBlock* prev = _head;
    while(p != nullptr) {
        if(p->size >= size) {
            if(p->size > size) {
                // 将内存块分裂成两个块
                auto* remains = reinterpret_cast<MemoryBlock*>(reinterpret_cast<char*>(p) + size + sizeof(MemoryBlock));
                remains->size = p->size - size - sizeof(MemoryBlock);
                remains->nxt = p->nxt;

                if(p == _head) {
                    _head = remains;
                } else {
                    prev->nxt = remains;
                }
            } else {
                // 将该内存块从空闲链表中移除
                if(p == _head) {
                    _head = p->nxt;
                } else {
                    prev->nxt = p->nxt;
                }
            }

            _free_mem_size -= size + sizeof(MemoryBlock);
            _used_mem_size += size;
            return reinterpret_cast<void*>(reinterpret_cast<char*>(p) + sizeof(MemoryBlock));
        }
        prev = p;
        p = p->nxt;
    }

    // 没有找到一个空闲块,申请一个新的内存块
    const size_t block_size = max(sizeof(MemoryBlock), size);
    const auto* new_block = reinterpret_cast<MemoryBlock*>(malloc(block_size));
    new_block->size = block_size - sizeof(MemoryBlock);
    new_block->nxt = _head;
    _head = new_block;
    _free_mem_size += new_block->size + sizeof(MemoryBlock);

    // 递归调用再次查找
    return Allocate(size);
}

步骤四:实现内存释放函数

最后,需要实现内存释放函数Free。该函数接受一个指向动态分配的内存的指针,并将内存块释放回空闲内存块列表中。这个函数实现很简单,只需要创建一个新的空闲内存块,并将其插入到空闲块列表的开头。

void MemoryManager::Free(void* p) {
    if(p == nullptr) {
        return;
    }

    auto* block_ptr = reinterpret_cast<MemoryBlock*>(reinterpret_cast<char*>(p) - sizeof(MemoryBlock));
    auto* p1 = _head;
    auto* prev = _head;

    while(p1 != nullptr && p1 < block_ptr) {
        prev = p1;
        p1 = p1->nxt;
    }

    block_ptr->nxt = p1;
    _free_mem_size += (block_ptr->size + sizeof(MemoryBlock));
    _used_mem_size -= block_ptr->size;

    if(prev == p1) {
        _head = block_ptr;
        return;
    }

    prev->nxt = block_ptr;
}

2. 示例说明

以下是两个示例,以演示如何使用我们的内存管理器来分配和释放内存:

示例一:分配字符串内存

int main() {
    MemoryManager mm;
    auto* str = reinterpret_cast<char*>(mm.Allocate(100));
    strcpy(str,"Hello");
    strcat(str," world!");
    cout << str << endl;
    return 0;
}

在这个例子中,我们使用mm.Allocate()函数来动态分配内存,并将其用于存储字符串"Hello world!"。

示例二:释放内存

int main() {
    MemoryManager mm;
    auto* str = reinterpret_cast<char*>(mm.Allocate(100));
    strcpy(str,"Hello");
    strcat(str," world!");
    cout << str << endl;
    mm.Free(str);
    return 0;
}

在这个例子中,我们首先使用mm.Allocate()函数来动态分配内存,然后使用mm.Free()函数来释放它。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:c++中的malloc底层实现代码 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • Java爬虫 信息抓取的实现

    Java爬虫可以通过模拟浏览器的行为,自动化地访问网页并抓取所需信息,主要分为以下几个步骤: 1. 简述Web爬虫的基本工作流程 1.1 网页访问 要抓取的信息一般都在网页中,因此第一步是访问目标网站。由于Java爬虫需要模拟浏览器的行为,因此一般使用java.net.HttpURLConnection或org.apache.http.client.Http…

    C 2023年5月23日
    00
  • Java日常练习题,每天进步一点点(38)

    Java日常练习题,每天进步一点点(38) 题目描述 定义父类People,创建子类VIP,编写一个测试类Test,在测试类里面,创建两个People的对象和两个VIP的对象并赋值,然后分别调用他们的属性与方法 题目思路 本题考察了Java面向对象的三大特性:封装、继承、多态。People作为父类,VIP作为子类,VIP拥有自己的新属性和方法。在测试类中,定…

    C 2023年5月23日
    00
  • C++小知识:不要去做编译器的工作

    C++小知识:不要去做编译器的工作 背景 C++ 是一个庞大的语言,语法实在复杂。由于各种优化等操作,编译器已经变得越来越强大,并且一些操作也很难手动实现。然而,很多 C++ 开发人员容易错误地尝试手动实现一些简单的编译器功能(示例包括手写字符串分割、手写代码验证、手写类型推断等)。本文会通过两个示例说明为什么需要避免做编译器工作。 示例一:手写类型推断 首…

    C 2023年5月23日
    00
  • visual studio 2019编译c++17的方法

    下面我将为您讲解如何在Visual Studio 2019中编译C++17,并提供至少两个示例。 1. 安装Visual Studio 2019 首先需要安装Visual Studio 2019,可以从官网下载安装包进行安装,安装包下载地址:https://visualstudio.microsoft.com/zh-hans/downloads/。 2. 开…

    C 2023年5月23日
    00
  • go语言异常panic和恢复recover用法实例

    下面是关于”Go语言异常panic和恢复recover用法实例”的详细攻略。 异常和panic 异常 异常是程序的非正常事件。当程序出现异常时,程序运行将被中断,控制流将进入一个异常处理程序来处理异常并防止程序崩溃。Go语言中的异常被称为panic。 panic 在Go语言中,panic函数被用于引发异常。当程序执行到panic()函数时,程序将会停止执行当…

    C 2023年5月22日
    00
  • json的定义、标准格式及json字符串检验

    JSON是JavaScript对象表示法(JavaScript Object Notation)的缩写,是一种轻量级的数据交换格式。 JSON的定义 JSON是一种用于数据交换的文本格式,和XML一样,JSON也是一种纯文本格式,可以轻松地在网络中传递。JSON使用JavaScript语法来描述数据,但是JSON仅仅是一种数据格式,不是一种编程语言,所以它是…

    C 2023年5月23日
    00
  • 浅谈go中cgo的几种使用方式

    浅谈 Go 中 Cgo 的几种使用方式 Cgo 是 Go 语言中的一个重要特性,它可以被用来在 Go 代码中调用 C 语言函数。Cgo 是 Go 语言最独特的特性之一,它可以让开发者直接使用 C 代码功能,也可以将 Go 代码转为 C 代码,这样就可以在 Go 中直接使用 C 库,同时也能够保证 Go 语言的安全性。 Cgo 中的基本使用 使用 Cgo 调用…

    C 2023年5月23日
    00
  • 03-变量\常量\进制

    变量和数据类型 所有定义的变量都存在内存中,定义变量需要内存空间,不同类型的变量需要的内存空间是不同的数据类型作用:告诉编译器,我这个数据在内存中需要多大的空间,编译器预算对象(变量)分配的内存空间大小。 1.常量与变量 1.1 常量 常量:程序运行中不能改变的量 整型常量:1 200 字符常量: ‘c’ 字符串常量:”hello” 实型常量(浮点型常量):…

    C语言 2023年4月18日
    00
合作推广
合作推广
分享本页
返回顶部