在C语言中,动态分配的内存是如何跟踪的

在C语言中,动态分配的内存是如何跟踪的,c,memory,C,Memory,我们使用malloc()在C中动态分配内存,并接收指向堆中某个位置的指针。 现在我们使用free()来释放内存,传递与argumnet相同的指针值 现在的问题是free()如何知道要释放多少。。考虑到我们总是可以调整malloc()分配的内存块的大小这一事实 这里是否有与哈希表相关的内容?典型的实现将在malloc返回的地址之前存储信息。这些信息将包括realloc或free执行其工作所需的信息,但具体存储内容的详细信息取决于实现。内存管理器使用表存储基于指针的附加数据,有时在指针之前,有时在别

我们使用malloc()在C中动态分配内存,并接收指向堆中某个位置的指针。 现在我们使用free()来释放内存,传递与argumnet相同的指针值

现在的问题是free()如何知道要释放多少。。考虑到我们总是可以调整malloc()分配的内存块的大小这一事实


这里是否有与哈希表相关的内容?

典型的实现将在malloc返回的地址之前存储信息。这些信息将包括realloc或free执行其工作所需的信息,但具体存储内容的详细信息取决于实现。

内存管理器使用表存储基于指针的附加数据,有时在指针之前,有时在别处。由于C非常简单,数据很可能是
pointer-2
pointer-4
,如
int
long
类型。正确的细节取决于编译器。

最初的技术是分配一个稍大的块,并在开始时存储大小,这是应用程序没有看到的部分。额外的空间包含一个大小,并且可能链接到将空闲块线程连接在一起以供重用

但是,这些技巧存在某些问题,例如缓存和内存管理行为差。在块中使用内存权限往往会不必要地分页,还会创建脏页,从而使共享和写时复制变得复杂

因此,更先进的技术是保留一个单独的目录。异国情调的方法也被开发出来,其中内存区域使用两种大小的相同功率


通常情况下,答案是:分配一个单独的数据结构来保持状态。

当我们使用malloc时,一个块将获得保留,其大小将略大于我们所请求的大小,作为对此malloc的回报,我们将获得指向该块开始的指针。 正如我告诉你们的那个样,这个块的大小将比你们所需要的稍微大一些。这个额外的空间将用于保持实际请求的块大小,指向下一个空闲块的指针,以及一些检查“若你们试图访问超过分配的块”的数据


因此,每当我们使用要释放的指针调用free时,这个free将搜索块空间中给定的额外信息,在块空间中它将获得要释放的最终大小。

一个简单的实现就是著名的第186-188页中的实现

我们得到的内存块实际上比我们申请的要大(结构头或联合头的大小)。结构可能如下所示:

typedef long Align;

union header
{
    struct 
    {
        union header* ptr;  // next block
        unsigned size;      // size of this block , times of head size  
    }s;
    Align x;
};
void free(void* ptr)
{
    Header *bp, *p;

    bp = (Header *)ptr - 1;

    /* .....                */
    /*return the memory to the linked list  */
}
一个图形来演示它:

调用
free
函数时,行为可能如下:

typedef long Align;

union header
{
    struct 
    {
        union header* ptr;  // next block
        unsigned size;      // size of this block , times of head size  
    }s;
    Align x;
};
void free(void* ptr)
{
    Header *bp, *p;

    bp = (Header *)ptr - 1;

    /* .....                */
    /*return the memory to the linked list  */
}
在VisualStudio中,我们有两个模型:
发布版本
调试版本
,我们甚至可以使用 用于存储调试消息的头,以简化调试。
debug version
中的头称为
\u CrtMemBlockHeader
,定义如下:

typedef struct _CrtMemBlockHeader
{
    struct _CrtMemBlockHeader * pBlockHeaderNext;
    struct _CrtMemBlockHeader * pBlockHeaderPrev;
    char *                      szFileName;
    int                         nLine;
    size_t                      nDataSize;
    int                         nBlockUse;
    long                        lRequest;
    unsigned char               gap[nNoMansLandSize];
} _CrtMemBlockHeader;
那么内存lalout是:


详细信息取决于C库,它是语言实现的一部分。编译器与此无关。此外,一些实现在内存池中保留由内存地址标识的小大小的块,因此这些块的大小不会保留在任何地方。哎呀,实际上大小保留在某个地方(并且必须是)。。。它保存在描述内存池的数据结构中。在最后存储大小将是一种毫无希望的无效策略。我不认为这是“异国情调”。呃,嘿,我想说些稍微不同的话。固定的!我在Linux内核中看到过类似的技术,内核内存分配使用存储在分配内存区域开头的“struct page”以及用于保持内存状态的结构。非常感谢您的介绍。。我读过《圣经》,完全错过了那个话题。你的描述完全有道理