我想知道malloc和free是怎么工作的。

int main() {
    unsigned char *p = (unsigned char*)malloc(4*sizeof(unsigned char));
    memset(p,0,4);
    strcpy((char*)p,"abcdabcd"); // **deliberately storing 8bytes**
    cout << p;
    free(p); // Obvious Crash, but I need how it works and why crash.
    cout << p;
    return 0;
}

如果答案是在记忆层面上的深度,如果可能的话,我会非常感激。


当前回答

这取决于内存分配器的实现和操作系统。

Under windows for example a process can ask for a page or more of RAM. The OS then assigns those pages to the process. This is not, however, memory allocated to your application. The CRT memory allocator will mark the memory as a contiguous "available" block. The CRT memory allocator will then run through the list of free blocks and find the smallest possible block that it can use. It will then take as much of that block as it needs and add it to an "allocated" list. Attached to the head of the actual memory allocation will be a header. This header will contain various bit of information (it could, for example, contain the next and previous allocated blocks to form a linked list. It will most probably contain the size of the allocation).

Free将删除头文件并将其添加回空闲内存列表。如果它与周围的自由块形成一个更大的块,这些块将被加在一起,形成一个更大的块。如果整个页面现在是空闲的,分配器很可能会将该页返回给操作系统。

这不是一个简单的问题。操作系统分配器部分完全不受您的控制。我建议您阅读Doug Lea的Malloc (DLMalloc)之类的东西,以了解一个相当快的分配器是如何工作的。

编辑:你的崩溃将由这样一个事实引起,即写大于分配,你已经覆盖了下一个内存头。这样,当它释放时,它会非常困惑,不知道它到底释放了什么,以及如何合并到下面的块中。这可能并不总是会直接导致免费的崩溃。这可能会导致以后的崩溃。一般避免内存覆盖!

其他回答

你的程序崩溃是因为它使用了不属于你的内存。它可能被其他人使用,也可能没有——如果你幸运的话,你崩溃了,如果没有,问题可能会隐藏很长一段时间,然后回来咬你一口。

就malloc/free实现而言——整本书都致力于这个主题。基本上,分配器会从操作系统中获得更大的内存块,并为你管理它们。分配器必须解决的一些问题是:

How to get new memory How to store it - ( list or other structure, multiple lists for memory chunks of different size, and so on ) What to do if the user requests more memory than currently available ( request more memory from OS, join some of the existing blocks, how to join them exactly, ... ) What to do when the user frees memory Debug allocators may give you bigger chunk that you requested and fill it some byte pattern, when you free the memory the allocator can check if wrote outside of the block ( which is probably happening in your case) ...

malloc/free的一个实现如下所示:

通过sbrk() (Unix调用)从操作系统获取一块内存。 在该内存块周围创建一个页眉和页脚,并提供一些信息,如大小、权限以及下一个和上一个块的位置。 当传入对malloc的调用时,引用一个指向适当大小的块的列表。 然后返回这个块,页眉和页脚也相应地更新。

理论上,malloc为这个应用程序从操作系统获取内存。然而,由于您可能只需要4个字节,而操作系统需要在页面上工作(通常是4k), malloc所做的要比这多一点。它取一个页面,并把它自己的信息放在那里,这样它就可以跟踪你从该页中分配和释放了什么。

例如,当分配4个字节时,malloc会提供一个指向4个字节的指针。您可能没有意识到的是,在4个字节之前的8-12个字节的内存被malloc用来构成您已分配的所有内存的链。当你调用free时,它会取你的指针,备份到它的数据所在的位置,并对其进行操作。

当你释放内存时,malloc将内存块从链上取下…并且可能会也可能不会将这些内存返回给操作系统。如果它这样做,那么访问内存可能会失败,因为操作系统将拿走你访问该位置的权限。如果malloc保留内存(因为它在该页中分配了其他内容,或者用于某些优化),则访问将正常工作。这仍然是错误的,但可能会起作用。

免责声明:我所描述的是malloc的一种常见实现,但绝不是唯一可能的实现。

同样重要的是要意识到,简单地使用brk和sbrk移动程序断点指针实际上并不分配内存,它只是设置了地址空间。例如,在Linux上,当访问该地址范围时,内存将由实际的物理页“备份”,这将导致页错误,并最终导致内核调用页分配器以获得备份页。

Malloc和free依赖于实现。典型的实现包括将可用内存划分为“空闲列表”——可用内存块的链表。许多实现人为地将它分为小对象和大对象。空闲块以内存块有多大以及下一个内存块在哪里等信息开始。

当你malloc时,一个块从空闲列表中拉出来。当你释放时,块被放回空闲列表。很有可能,当你重写指针的末尾时,你是在写空闲列表中一个块的头。当您释放内存时,free()尝试查看下一个块,并可能最终击中导致总线错误的指针。