如果新块大小小于初始块,我应该强制执行realloc检查吗?

在这种情况下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; 

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

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

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

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

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

realloc函数解除分配ptr指向的旧对象,并返回指向具有size指定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的返回值是一个好习惯(规范并不表示如果缩小内存块比放大内存块更安全)。 但是你应该注意不要丢失初始指针(在你的情况下你做的),因为你完全无法释放它。

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; 

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

与在realloc()中花费的时间相比,进行检查所花费的时间非常少,我甚至无法理解为什么它会成为一个问题。 或者你想减少代码行数?

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)范例。