在调用前确定realloc()行为

在调用前确定realloc()行为,c,memory-management,C,Memory Management,据我所知,当要求保留较大的内存块时,realloc()函数将执行三种不同的操作之一: 如果存在空闲连续块 增长当前块 否则,如果内存不足 分配新内存 将旧内存复制到新内存 释放旧内存 其他的 返回空 扩大当前区块是一项非常廉价的操作,因此这是我想利用的行为。但是,如果我重新分配内存是因为我想(例如)在现有字符串的开头插入一个字符,那么我不希望realloc()复制内存。我将使用realloc()复制整个字符串,然后再次手动复制以释放第一个数组元素 是否可以确定realloc()将做什么?如果

据我所知,当要求保留较大的内存块时,realloc()函数将执行三种不同的操作之一:


如果存在空闲连续块
增长当前块
否则,如果内存不足
分配新内存
将旧内存复制到新内存
释放旧内存
其他的
返回空

扩大当前区块是一项非常廉价的操作,因此这是我想利用的行为。但是,如果我重新分配内存是因为我想(例如)在现有字符串的开头插入一个字符,那么我不希望realloc()复制内存。我将使用realloc()复制整个字符串,然后再次手动复制以释放第一个数组元素

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

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


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

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

realloc()
的行为可能取决于其特定的实现。基于这一点编写代码将是一个可怕的黑客行为,至少可以说,这违反了封装

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

  • 查找当前缓冲区的大小
    • 分配一个比前一个缓冲区大的新缓冲区(使用
      malloc()
    • 将所需前缀复制到新缓冲区
    • 将前一个缓冲区中的字符串复制到新缓冲区,从前缀后开始
    • 释放上一个缓冲区

  • 如果它们很适合您的内存分配需要,您可以使用它们的功能。obstack是glibc的一项功能,但它们也可以在库中获得,这是相当方便的。

    如评论中所述,问题中的案例3(无内存)是错误的<如果没有可用内存,code>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()内存时,您将始终获得一个新地址


    正如其他人所指出的,如果您关心这个问题,也许是时候看看您的算法了。

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

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


    使用此方法,您还可以右对齐缓冲区中的字符串,以便在开始处添加字符。

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

    char* buf = malloc(1024);
    char* start = buf + 1024 - 3;
    start[0]='t';
    start[1]='o';
    start[2]='\0';
    
    要将“on”添加到字符串的开头,使其成为“on\0”,请执行以下操作:

    start-=2;
    如果(启动
    这样,您就不必在每次开始插入时都复制缓冲区


    如果必须在开头和结尾都进行插入,只需在两端分配一些空间;中间插入仍然需要你洗牌,显然,

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

    另一个进程将在其地址空间中分配它,因此它不相关。您的3个案例中的最后一个是不正确的,如果内存不足,realloc将返回空指针,而不是您传递的指针。正如Robert Gamble所说-realloc()在没有内存的情况下返回NULL。我想建议第一个条件应该是“如果存在可用的连续块,并且组合块足够大”或类似的东西会非常挑剔……这只是不符合逻辑,为什么不使用realloc并至少在某些情况下获得性能改进呢,你的建议会永远记住的。他希望提高性能,在不需要的时候避免使用memcpy。这实际上是我目前实施的解决方案(尽管我每次都超额分配,以减少将来的重新分配次数)。正如Ilya所说,我一直在寻找一个更为优化的解决方案,但正如您所指出的,我可能已经得到了最好的折衷方案。如果您想在开始时插入de char,则必须使用memmov()after重新分配缓冲区。。。如果realloc()无法增长当前块,您将复制内存两次,一次是realloc(),一次是memmov()Romulo的解决方案始终复制内存,但仅复制一次…;)请注意,
    memmove
    可能比
    memcpy
    慢得多,特别是当偏移量为1字节时,因此在可以安全使用
    memcpy
    的地方分配一个新的缓冲区总是最好的
    char* buf = malloc(1024);
    char* start = buf + 1024 - 3;
    start[0]='t';
    start[1]='o';
    start[2]='\0';
    
    start-=2;
    if(start < buf) 
      DO_MEMORY_STUFF(start, buf);//time to reallocate!
    start[0]='o';
    start[1]='n';