我是不是应该强制重新分配检查新块大小是不是小于初始块?

Posted

技术标签:

【中文标题】我是不是应该强制重新分配检查新块大小是不是小于初始块?【英文标题】:Should I enforce realloc check if the new block size is smaller than the initial?我是否应该强制重新分配检查新块大小是否小于初始块? 【发布时间】:2011-02-02 05:30:04 【问题描述】:

在这种情况下 realloc 会失败吗?

int *a = NULL;

a = calloc(100, sizeof(*a));
printf("1.ptr: %d\n", a);

a = realloc(a, 50 * sizeof(*a));
printf("2.ptr: %d\n", a);

if(a == NULL)
    printf("Is it possible?\n");


return (0);

我的输出是:

1.ptr: 4072560
2.ptr: 4072560

所以'a'指向同一个地址。 那么我应该强制执行 realloc 检查吗?

稍后编辑

在 Windows XP 下使用 MinGW 编译器。 Linux 上的行为是否与 gcc 类似?

后期编辑 2: 这样检查可以吗?

int *a = NULL, *b = NULL;

a = calloc(100, sizeof(*a));
b = realloc(a, 50 * sizeof(*a));

if(b == NULL)
    return a;

a = b;
return a;

【问题讨论】:

这是***.com/questions/1736433/… 的副本,但我认为这是一个更好的问题和答案。 【参考方案1】:

是的,您应该始终对 realloc 或任何其他内存分配进行检查。

当前重复使用相同地址的行为是不应依赖的实现细节。这样做只会在库切换其实现或迁移到新平台时为自己打开错误。

这可能会失败吗?可能不会,如果你能找到这样的案例,我会感到震惊。然而,这并不意味着它不会。将 realloc 包装在一个自动检查每个操作的函数中非常简单,没有理由不这样做。

void* xrealloc(void* ptr, size_t size) 
  ptr = realloc(ptr, size);
  if ( !ptr ) 
    exit(EXIT_FAILURE);
  
  return ptr;

【讨论】:

你能举一个小内存补丁重新分配失败的例子吗? @hanno,我不能,坦率地说,如果可以的话,我会感到惊讶。但是用它作为它不会失败的证据是在无知地争论。我更喜欢使用包装函数来检查所有情况下的返回。这样做更安全。 @nomemory 您的支票是有效的,并且在我看来可以,假设返回更大尺寸的 a 是可以的。 @nomemory 如果 realloc 失败,它不应该改变原始指针的内容。所以 a 仍然会指向 calloc 的返回值 @hanno:如果您的 malloc 实现使用 bibop 分配器并且较小的大小因此需要进入新页面,但页面分配器无法分配页面,则 realloc 可能会失败。当然,智能分配器可能会选择不释放旧块而只是返回它,但可以想象它可以返回 NULL【参考方案2】:

在任何情况下检查realloc 的返回值是一个好习惯(规范并没有说缩小内存块比扩展它更安全)。但是你应该小心不要丢失初始指针(在你的情况下你会丢失),因为你将完全无法释放它。

【讨论】:

【参考方案3】:

C99 标准 §7.20.3.4 (realloc) 说:

realloc 函数释放 ptr 指向的旧对象并返回一个 指向具有 size 指定大小的新对象的指针。新的内容 对象应与释放前的旧对象相同,直至两者中的较小者 新旧尺寸。新对象中超出旧对象大小的任何字节都有 不确定的值。

如果 ptr 是空指针,则 realloc 函数的行为类似于 malloc 函数 规定的大小。否则,如果 ptr 与先前返回的指针不匹配 calloc、malloc 或 realloc 函数,或者如果空间已被调用释放 对于 free 或 realloc 函数,行为是未定义的。如果记忆为新 对象不能被分配,旧对象没有被释放,它的值不变。

返回

realloc 函数返回一个指向新对象的指针(可能具有相同的 值作为指向旧对象的指针),或者如果新对象不能是空指针 已分配。

注意旧对象已被释放;新对象可能恰好指向与旧对象相同的位置。而且可能会出现问题。这不太可能,但遵循“始终”的规则要比出现奇怪的例外要简单得多。

通常的反驳是“如果这不能失败,这意味着我有一个我无法测试的错误路径”。在某种程度上,这是真的。但是,可能是内存受到了一些践踏,导致分配无法成功——因为控制信息已损坏。更有可能你只会得到一个核心转储,但也许代码足够健壮,可以避免这种情况。 (我假设硬编码的 100 和 50 是为了提出问题;现实生活中的代码在知道它真正需要多少时不会过度分配。)

两个对“realloc()”的调用是相邻的,就像这里一样,几乎没有任何出错的余地。然而,现实生活中的工作代码会在两者之间进行一些操作——并且该代码可能会导致第二个“realloc()”失败。

关于您的“编辑 2”...

代码最好写成:

if (b != NULL)
    a = b;
return a;

但基本概念还可以。请注意,标准明确规定,如果无法创建新分配,则原始分配是安全的。

【讨论】:

感谢您的回答。代码是“虚拟的”,只是为了理解概念。【参考方案4】:

如果 realloc 在传递小于原始分配的大小时失败,这将是令人惊讶的,但 C 标准 (7.20.3.4) 中没有任何内容可以保证它总是成功:

realloc 函数释放 ptr 指向的旧对象和 返回一个指向新对象的指针 具有 size 指定的大小。这 新对象的内容应为 与旧对象相同 在重新分配之前,直到 新旧尺寸较小。任何 新对象中的字节超出 旧对象的大小有 不确定的值。

如果 ptr 是空指针,则 realloc 函数的行为类似于 malloc 指定大小的函数。 否则,如果 ptr 不匹配 之前返回的指针 callocmallocrealloc 函数, 或者如果空间已被释放 致电 freerealloc 函数,行为未定义。 如果新对象的内存不能 已分配,旧对象未分配 解除分配,其值为 不变。

退货

realloc 函数返回一个指针 到新对象(可能有 与指向旧指针的值相同 对象),如果是新的,则为空指针 无法分配对象。

realloc 的一个非常简单的一致性实现是这样的:

void *realloc(void *ptr, size_t size)

    void *new_ptr= malloc(size);
    if (new_ptr && ptr)
    
        size_t original_size= _get_malloc_original_size(ptr);
        memcpy(new_ptr, ptr, min(original_size, size));
        free(ptr);
    

    return new_ptr;

在内存不足的情况下(或malloc 将返回NULL 的任何条件),这将返回NULL

如果原始分配的大小大于或等于请求的大小,返回相同的指针也是一个非常简单的优化。但 C 标准中没有任何规定。

【讨论】:

嗯...它在哪里说缩小的 realloc 总是会成功?我引用相同的文本并得出相反的结论。我担心我们中的一个人是错的。 @Jonathan Leffler:嗯...是的,它在哪里这么说?我认为你们俩在同一条船上。 @Jonathan,我说“如果 realloc 在传递小于原始分配的大小时失败,那将是令人惊讶的”,而不是缩小 realloc 总是会成功。 :) 我想措辞很微妙,足以混淆。 如果要求将内存块重新分配为零大小,那么符合标准的实现是否会失败(留下分配的旧内存)?由于在成功的情况下返回 null 是合法的,是否还有其他方法可以确定仍需要释放内存块? @supercat,是的,理论上可以。这实际上取决于malloc(0) 是否返回空指针。如果是,那么您无法确定realloc(ptr, 0) 是否成功。如果没有,那么您可以:success= (realloc(ptr, 0)!=0).【参考方案5】:

与在 realloc() 中花费的时间相比,检查所需的时间是如此之少,以至于我什至不明白为什么会出现问题。还是要减少代码行数?

【讨论】:

【参考方案6】:

realloc() 可以很容易地返回 NULL 以减小大小。

void *ptr = malloc(10);
ptr = realloc(ptr, 0);
if (ptr == NULL) 
  puts("Failure because return value is NULL? - not really");

realloc(any_pointer, 0) 可以返回 NULL 或者一些 not-NULL 指针,它是由实现定义的。

这就是为什么realloc()/malloc()失败不应该是if (ptr == NULL)的简单测试而是

void *ptr = malloc(newsize); // or realloc(..., newsize)
if (ptr == NULL && newsize > 0) 
  exit(0); // Handle OOM;

由于这种歧义,如果代码想要制作一个realloc() 包装器,推荐类似:

void *xrealloc(void *ptr, size_t newsize, bool *falure) 
  *failure = 0;
  if (newsize > 0) 
    void *tmp = realloc(ptr, newsize);
    if (tmp == NULL) 
      *failure = 1;
      return ptr;  // old value
    
    return tmp;  // new value
   
  free(ptr);
  return NULL; // new value
  

realloc() 上获得NULL 并减小尺寸因此并不是真正的失败,所以这个答案只是切线适用,但 OP 的问题是“......强制重新分配检查新的块大小小于初始大小?”然后使用不太可信的if (ptr == NULL) 范式。

【讨论】:

以上是关于我是不是应该强制重新分配检查新块大小是不是小于初始块?的主要内容,如果未能解决你的问题,请参考以下文章

[转] Webpack 插件 — SplitChunksPlugin

如果我重新分配并且新大小为 0,会发生啥情况。这是不是等同于免费?

我是不是应该检查并释放 VLA 类的分配运算符 (operator=) 中的指针

是否可以强制 UIPopoverPresentationController 重新定位弹出框而不是调整其大小?

如何检查文件大小是不是超过批处理的最大文件大小[重复]

检查指针是不是已分配内存