6

据我了解,当被要求保留更大的内存块时, realloc() 函数将执行以下三种不同的操作之一:

if free contiguous block exists
    grow current block
else if sufficient memory
    allocate new memory
    copy old memory to new
    free old memory
else
    return null

增长当前块是一个非常便宜的操作,所以这是我想利用的行为。但是,如果我重新分配内存是因为我想(例如)在现有字符串的开头插入一个字符,我不希望 realloc() 复制内存。我最终将使用 realloc() 复制整个字符串,然后再次手动复制它以释放第一个数组元素。

是否可以确定 realloc() 会做什么?如果是这样,是否有可能以跨平台的方式实现?

4

8 回答 8

7

realloc()的行为可能取决于其具体实现。并且基于您的代码将是一个可怕的黑客攻击,至少可以说,它违反了封装。

对于您的具体示例,更好的解决方案是:

  1. 查找当前缓冲区的大小
    • 分配一个新的缓冲区(带malloc()),大于前一个
    • 将您想要的前缀复制到新缓冲区
    • 将前一个缓冲区中的字符串复制到新缓冲区,从前缀之后开始
    • 释放前一个缓冲区
于 2008-10-19T14:08:53.350 回答
2

如评论中所述,问题中的案例 3(没有记忆)是错误的;realloc()如果没有可用内存,将返回 NULL [问题现已修复]。

Steve McConnell 在“代码完成”中指出,如果您在失败realloc()时将返回值保存在原始指针的唯一副本中realloc(),那么您只是泄漏了内存。那是:

void *ptr = malloc(1024);
...
if ((ptr = realloc(ptr, 2048)) == 0)
{
    /* Oops - cannot free original memory allocation any more! */
}

realloc() 的不同实现会表现不同。唯一安全的假设是数据总是会被移动——当你 realloc() 内存时你总是会得到一个新地址。

正如其他人指出的那样,如果您对此感到担忧,也许是时候看看您的算法了。

于 2008-10-19T14:47:51.680 回答
1

将字符串向后存储有帮助吗?

否则......只需 malloc() 比您需要的更多空间,当您用完空间时,复制到新缓冲区。一个简单的技巧是每次将空间加倍;这很有效,因为字符串越大(即复制到新缓冲区所需的时间越多),它需要发生的频率就越低。

使用这种方法,您还可以在缓冲区中右对齐您的字符串,因此很容易将字符添加到开头。

于 2008-10-28T05:39:18.220 回答
0

——如果你仔细想想,它是行不通的。在你检查它要做什么和实际做之间,另一个进程可以分配内存。 在多线程应用程序中,这是行不通的。在你检查它要做什么和实际做这件事之间,另一个线程可以分配内存。

如果您担心这类事情,可能是时候查看您正在使用的数据结构,看看是否可以解决问题。根据这些字符串的构造方式,您可以使用设计良好的缓冲区非常有效地完成此操作。

于 2008-10-19T13:37:14.570 回答
0

我认为跨平台的方式是不可能的。 是 ulibc 实现的代码,它可能会为您提供如何以平台相关方式进行操作的线索,实际上最好找到 glibc 源代码,但这个代码位于谷歌搜索之上 :)

于 2008-10-19T13:44:49.423 回答
0

如果obstack非常适合您的内存分配需求,您可以使用它们快速增长的功能。Obstacks 是 glibc 的一个特性,但它们也可以在libiberty库中使用,它是相当可移植的。

于 2008-10-19T14:46:45.630 回答
0

为什么不在字符串的左侧保留一些空的缓冲区空间,如下所示:

char* buf = malloc(1024);
char* start = buf + 1024 - 3;
start[0]='t';
start[1]='o';
start[2]='\0';

要将“on”添加到字符串的开头以使其成为“onto\0”:

start-=2;
if(start < buf) 
  DO_MEMORY_STUFF(start, buf);//time to reallocate!
start[0]='o';
start[1]='n';

这样,您不必在每次想要在开始时进行插入时都继续复制缓冲区。

如果您必须在开头和结尾都进行插入,则只需在两端分配一些空间即可;显然,中间的插入仍然需要你随机播放元素。

于 2008-10-28T06:04:01.057 回答
0

更好的方法是使用链表。将您的每个数据对象分配在一个页面上,然后分配另一个页面并具有指向它的链接,可以来自上一页或来自索引页面。这样您就知道下一次分配何时失败,并且您永远不需要复制内存。

于 2010-10-07T03:04:20.603 回答