realloc函数在不释放旧内存的情况下重新分配内存

9

我希望使用realloc来增加内存大小,同时保持指针不变(因为调用者在使用它)。然而,realloc并不总是这样做;有时会返回一个不同的指针并释放旧的指针。我想要“尝试”realloc内存,如果不可能,就回退到使用原始指针的不同方法 - 但是realloc已经摧毁了那个指针!

是否有一种方法可以尝试增加malloc分配的内存,而不破坏旧指针(像realloc那样)?

例如:

void *pold;
void *pnew = realloc(pold, newsize);
if (pnew != pold)
{
     free(pnew);
     DoDifferently(pold); // but pold is freed already
}

附言:我不关心可移植性(仅限linux,因此有该标签)。


5
简短回答:不可以(至少在可移植代码中是不行的——你可能可以深入标准库并调用一些未公开的内部函数来在某个特定版本的标准库中实现它)。 - Jerry Coffin
我认为这是一个重复的问题。类似但不完全相同的问题:https://dev59.com/T1nUa4cB1Zd3GeqPYj6V - Pubby
1
@Joe:是的,他的意思是尝试在原地重新分配内存,如果不可能,就失败。 - Jerry Coffin
@Joe 希望有这样一个函数原型:bool tryrealloc(char * basePtr, size_t newSize); 如果内存可以被扩展,则此函数返回True,如果无法扩展,则返回False,但不要破坏原始内存的内容(不做任何事情)。 - benjarobin
为了在扩展区域时保持指针完全相同,需要确保对象之后有足够的空闲空间。运行时系统不能收集其他对象并将它们移动以腾出空间,因为这会在程序不知情的情况下改变它们的地址。 - vonbrand
我知道我晚了5年才来到这个游戏,但你总是可以编写自己的薄层在malloc()之上执行大型分配,然后使用一组类似的函数在这些更大的分配中分配内存。基本上,管理自己的内存分配。然后,您可以按照自己的设计API,并仍然使其跨平台。 - Qix - MONICA WAS MISTREATED
7个回答

4
你应该查看正在使用的libc中realloc()的源代码。从那里,很容易看出在可以原地增加大小时所遵循的路径,以及返回新指针的else情况。然后,使用它来编写自己的tryrealloc()函数。
例如,这是来自uclibc的realloc()源代码:http://cristi.indefero.net/p/uClibc-cristi/source/tree/nptl/libc/stdlib/malloc/realloc.c
24  void *
25  realloc (void *mem, size_t new_size)
26  {
...
57    if (new_size > size)
58    /* Grow the block.  */
59    {
60        size_t extra = new_size - size;
61  
62        __heap_lock (&__malloc_heap_lock);
63        extra = __heap_alloc_at (&__malloc_heap, base_mem + size, extra);
64        __heap_unlock (&__malloc_heap_lock);
65  
66        if (extra)
67          /* Record the changed size.  */
68          MALLOC_SET_SIZE (base_mem, size + extra);
69        else
70          /* Our attempts to extend MEM in place failed, just
71             allocate-and-copy.  */
72        {
73          void *new_mem = malloc (new_size - MALLOC_HEADER_SIZE);
74          if (new_mem)
75            {
76              memcpy (new_mem, mem, size - MALLOC_HEADER_SIZE);
77              free (mem);
78            }
79          mem = new_mem;
80        }
81      }
...

我已删除一些内容以使其更清晰。但是您可以看到在第66行,它检查是否可以简单地增加当前指针的内存。这是您想要保留的部分。从第69行开始的else情况是为了处理将释放旧内存并返回新指针的情况。这是您想要排除并以不同方式处理的部分。根据您的说法,我猜您只想删除第77行,它执行释放操作。
如果您选择这种方法,请记住,您必须手动释放旧指针或新指针,因为两者现在都有效(您不希望出现内存泄漏)。
此外,这是针对uclibc的。如果您已经使用不同的libc,则应该基于该libc的realloc()函数来构建新的tryrealloc()函数。
编辑:如果您采用此方法,必须小心。您将基于内存管理器的内部实现来构建解决方案,因此在各种libc实现之间以及相同libc的不同版本之间可能会有所不同。因此,请谨慎考虑并注意相关警告。

那么您建议他使用__heap_alloc_at(如果可用)吗?使用内部函数并不是一个好主意,因为它们不仅在库之间发生变化,而且在同一库的不同版本之间也会发生变化。它今天可能有效,但很可能明天就无效了。 - Shahbaz
@Shahbaz 不,我建议他查看他正在使用的libc版本,并根据该libc的realloc()函数编写自己的tryrealloc()函数。但是不同版本的相同库可能会发生变化,因此我也会加上警告。 - Laurent Parenteau

1
替代内存管理库jemalloc提供了这种功能。使用xallocx()可以这样做:
size_t realsize = xallocx(pold, newsize, 0, 0);
if (realsize < newsize)
{
     DoDifferently(pold);
}

1

我认为没有一个可移植的 realloc 类型函数可以实现这个功能。

一个相对简单的可移植解决方案是预先分配一个比你最初需要的更大的内存块。这样可以允许一些增长而不改变指针(实际上,你将在现场执行自己的 realloc)。

一旦你超过了原始块,你就必须在其位置分配一个新的。这是你的“失败”情况。


1

这个问题没有便携式的解决方案,而非便携式的解决方案也不是没有风险。

非便携式的解决方案是使用与GNU malloc一起工作的malloc_usable_size来查找内存区域实际大小。然而,即使内存区域足够大,我也不确定realloc是否保证使用它。我IRC,曾经有一个选项导致realloc总是分配新内存,但我现在找不到它了;虽然我没有很努力地找。


1
据我所知(使用标准库以实现可移植性),这是不可能的。然而,有一些简单的解决方法。由于您的调用者使用该指针,您必须为他们修复它。其中一个解决方案是:
void *do_something(void *your_pointer)
{
    void *new_pointer;
    new_pointer = realloc(your_pointer, ...);
    /* more logic */
    return new_pointer;
}

并告诉他们要像这样使用do_something

new_pointer = do_something(my_pointer);
if (new_pointer) /* if returning NULL is at all possible */
    my_pointer = new_pointer;

或者,您可以自行处理:

int do_something(void **pointer_to_your_pointer)
{
    void *new_pointer;
    new_pointer = realloc(*pointer_to_your_pointer, ...);
    /* more logic */
    *pointer_to_your_pointer = new_pointer;
    return error_code;
}

并告诉他们要这样使用:

do_something(&my_pointer);

不,API是固定的,我必须保持指针不变。 - queen3

0

程序员无法重新分配相同指向的内存区域...你只能使用realloc()增加大小...使用realloc()后可能会得到不同的内存位置...大多数情况下可能不会有任何问题,即使内存位置发生变化...


0
为什么不直接使用 malloc 分配可能需要的最大内存呢?只有在使用时,内存才会实际分配。要进行微调,您可以使用 mmap 来分配一块内存区域。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接