【问题标题】:How is realloc implemented in the C standard library?C标准库中realloc是如何实现的?
【发布时间】:2019-12-17 15:57:24
【问题描述】:

我找不到 realloc 函数的任何源代码,它似乎违反了 C 的基本规则:重新分配不需要内存的长度。

  1. 如何在不知道原始内存长度的情况下重新分配内存?

  2. 如何自己实现这个功能?

【问题讨论】:

  • malloc 分配的内存通常有一个包含此信息的标头,通常位于内存中 malloc 返回的指针之前(实现可能不同,但这是一种常见的方法)。此信息是 C 库的内部信息。这就是允许free 在不使用大小参数的情况下工作的原因。如果你想实现realloc,你可能也想实现malloc和朋友们。
  • @JL2210 在单个文件中实现真实、完整和高效(能够实际调整块大小,包括mremap 的使用)。
  • reallocfree 仅在传递的指针用于先前使用相关分配例程分配的内存(或为空指针)时才定义。这些例程一起工作以保存有关它们提供的分配的信息。因此,当传递指向reallocfree 的指针时,您不需要传递已分配内存的长度,因为例程已经拥有该信息。
  • 对于awkwardly written, but good basic malloc/realloc introduction 工作,虽然这个 32 位教程(小心你可以将它扩展到处理 64 位)请注意有许多策略来处理内存块,例如使用 using “边界标记”。例如。见Paper by Paul Wilson
  • K&R中有mallocreallocfree的示例代码。

标签: c realloc


【解决方案1】:

至少有 3 个考虑因素使得完全在“userland”C 中实现 realloc 变得困难或不可能。

  1. mallocfree 使用的相同数据结构进行交互。

  2. mallocrealloc 需要返回适合任何对象对齐的存储。在 C11 之前,我们还没有所有必要的宏来 100% 便携地计算这种对齐方式。

  3. mallocrealloc 不能在可移植 C 中实现是一个棘手的语言法律原因。我可能在这里混淆了术语,但问题是在函数计算出指针值之后它将返回,它无法放弃与存储相关联的“有效类型”。因此,即使您可以让您的版本正常工作,它也是事实上非标准的。

【讨论】:

  • naah,3 有点偏离...问题是您不能执行从 char [] 返回字节的 malloc,因为它具有声明的类型,而 C 标准不提供任何返回没有声明类型的字节的方法除了malloc & al 那么如果你正在编写自己的malloc 并且不依赖它们,你从哪里获得这些无类型字节:D 有效类型不会与存储永久关联,你总是可以通过赋值来覆盖它!
  • "如果通过非字符类型的左值将值存储到没有声明类型的对象中,则左值的类型成为对象的有效类型用于该访问和不修改存储值的后续访问。”
【解决方案2】:

考虑一个例子(在 c 中):

#include <stdio.h>
#include <stdlib.h>
struct A {
    int x[10];
    int y[1];
};

int main()
{
  struct A *ptr = calloc(1, sizeof(struct A));
  ptr->x[10] = 4;
  printf("%i\n", ptr->x[10]);
  ptr = realloc(ptr, 80);  // realloc
  ptr->x[20] = 4;
  printf("%i\n", ptr->x[20]);
  free(ptr);
  return 0;
}

现在考虑以下汇编代码(与 realloc 函数相关)(如果您有兴趣,当然可以):

  400604:       48 8b 45 f8             mov    rax,QWORD PTR [rbp-0x8]
  400608:       be 50 00 00 00          mov    esi,0x50
  40060d:       48 89 c7                mov    rdi,rax
  400610:       e8 bb fe ff ff          call   4004d0 <realloc@plt>
  400615:       48 89 45 f8             mov    QWORD PTR [rbp-0x8],rax

现在,在这里,指针ptr 被移入寄存器rax,最终移入寄存器esi。以及要重新分配到寄存器rdi 中的指针的大小。第 4 行是对realloc@GLIBC 函数的调用。这里的指针ptr 必须先前由malloccalloc(在这种情况下)和realloc 函数分配。在最后一行,rax 包含指向新内存区域的指针(由realloc 返回),该指针分配给指针ptr(注意,rbp-0x8 是指针在堆栈上的位置)。

在不知道原始内存长度的情况下如何重新分配内存?

标准库(e.g. library code used)中的这个realloc函数定义为:

void * __libc_realloc (void *oldmem, size_t bytes)

这里,*oldmem 是指向旧内存的指针,在我们的例子中是指针 ptrbytes 是要分配的大小,在我们的例子中是 80。这个函数有很多检查,比如检查指针是否为null,如果是,则将其视为简单的malloc函数,检查字节是否为0,如果是则将其视为free等。我们对以下几行感兴趣:

  /* chunk corresponding to oldmem */
  const mchunkptr oldp = mem2chunk (oldmem);
  /* its size */
  const INTERNAL_SIZE_T oldsize = chunksize (oldp);

这意味着所有信息都保存在内部(在 malloc 程序头中,正如前面在 cmets 中的 @SirDarius 所指出的那样),即每个指针及其相关的大小。如果一切顺利,那么它会调用另一个函数(在这种情况下):

_int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
             INTERNAL_SIZE_T nb)

此函数分配内存中的实际大小。并返回一个 new_pointer,它最终由__libc_realloc 返回,我们在寄存器rax 中收到它。

我怎样才能自己实现这个功能?

有兴趣可以看完整代码here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-06
    • 1970-01-01
    • 2020-01-07
    • 2015-05-18
    • 1970-01-01
    • 2017-10-20
    • 1970-01-01
    • 2012-06-07
    相关资源
    最近更新 更多