在调用 realloc() 之前确定其行为

问题描述 投票:0回答:8

据我了解,当被要求保留更大的内存块时,

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()
将做什么?如果可以的话,是否可以跨平台的方式实现?

c memory-management
8个回答
7
投票

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

适合您的具体示例的更好解决方案是:

  1. 查找当前缓冲区的大小
    • 分配一个新的缓冲区(使用
      malloc()
      ),大于之前的缓冲区
    • 将你想要的前缀复制到新缓冲区
    • 将前一个缓冲区中的字符串复制到新缓冲区,从前缀之后开始
    • 释放之前的缓冲区

2
投票

正如评论中指出的,问题中的情况3(没有记忆)是错误的;如果没有可用内存,

realloc()
将返回 NULL [问题现已修复]。

Steve McConnell 在《Code Complete》中指出,如果在

realloc()
失败时将
realloc()
的返回值保存在原始指针的唯一副本中,那么您就泄漏了内存。那就是:

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

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

正如其他人指出的,如果您担心这一点,也许是时候看看您的算法了。


1
投票

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

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

使用此方法,您还可以在缓冲区中右对齐字符串,因此可以轻松在开头添加字符。


0
投票

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


0
投票

-如果你仔细想想,这是行不通的。在您检查它将要执行的操作和实际执行操作之间,另一个进程可能会分配内存。 在多线程应用程序中这是行不通的。在您检查它将要执行的操作和实际执行操作之间,另一个线程可以分配内存。

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


0
投票

为什么不在字符串的左侧保留一些空的缓冲区空间,就像这样:

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

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

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

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

如果必须在开头和结尾都插入,只需在两端分配一些空间即可;显然,在中间插入仍然需要您重新排列元素。


0
投票

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


0
投票

我认为跨平台方式是不可能的。 这里是 ulibc 实现的代码,它可能会给你一个如何以平台相关的方式执行它的线索,实际上最好找到 glibc 源代码,但这个是在谷歌搜索的顶部:)

© www.soinside.com 2019 - 2024. All rights reserved.