重新分配行为的可移植性 概要说明退货

问题描述

我一直在编写在许多循环中分配数据的代码,发现使用 realloc() 非常方便,因为它可以一致地处理初始和中间循环数据,而无需添加释放和分配新指针的条件。

我写了这个小测试程序:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    char *x = NULL;
    x = realloc (x,0);
    printf ("Address of realloc (NULL,0): %p\n",x);
    assert (x != NULL);
    printf ("Address of realloc (x,8): %p\n",x);
    x = realloc (x,8);
    assert (x != NULL);
    x = realloc (x,0);
    assert (x == NULL);
    printf ("Address of realloc (x,21);
    printf ("Address of realloc (x,21): %p\n",x);
    assert (x != NULL);
    x = realloc (x,0); // free.
    assert (x == NULL);
    printf ("Address of realloc (x,x);

    return 0;
}

并进行内存检查:

$ gcc -Wall -g test_realloc.c -o test_realloc.o
$ valgrind ./test_realloc.o 
==1192613== Memcheck,a memory error detector
==1192613== copyright (C) 2002-2017,and GNU GPL'd,by Julian Seward et al.
==1192613== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==1192613== Command: ./test_realloc.o
==1192613== 
Address of realloc (NULL,0): 0x4a3a040
Address of realloc (x,8): 0x4a3a040
Address of realloc (x,0): (nil)
Address of realloc (x,21): 0x4a3a510
Address of realloc (x,0): (nil)
==1192613== 
==1192613== HEAP SUMMARY:
==1192613==     in use at exit: 0 bytes in 0 blocks
==1192613==   total heap usage: 4 allocs,4 frees,1,053 bytes allocated
==1192613== 
==1192613== All heap blocks were freed -- no leaks are possible
==1192613== 
==1192613== For lists of detected and suppressed errors,rerun with: -s
==1192613== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

到目前为止,很好。唯一需要注意的是必须初始化初始值(即使是 NULL)。

现在我想知道它跨平台的可移植性如何。 Linux x86_64 上的 man 3 realloc 对这种行为非常明确,除了 realloc (NULL,0) 在本例中意外产生非空地址。

我可以在多大程度上依赖这种行为?

编辑:确切地说,我的问题是我是否可以依靠我的示例程序来避免任何平台上的 UB 或内存泄漏。

谢谢。

解决方法

这就是 C11 标准(更准确地说,它是一个草案,但它是最接近公开标准的标准)说:

概要

    #include <stdlib.h>
    void *realloc(void *ptr,size_t size);

说明

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

如果 ptr 是空指针,则 realloc 函数的行为类似于指定大小的 malloc 函数。否则,如果 ptr 与之前由内存管理函数返回的指针不匹配,或者如果空间已通过调用 free 或 realloc 函数释放,则行为未定义。如果不能为新对象分配内存,则旧对象不会被释放,其值不变。

退货

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

https://port70.net/~nsz/c/c11/n1570.html#7.22.3.4

所以 realloc (NULL,0)malloc(0) 相同,它可能返回一个不能取消引用的非空指针。

,

在询问“这种行为”时,您的问题并没有具体说明您对哪种行为感兴趣。据推测,您感兴趣的是 realloc 是否可能返回 realloc(x,0) 的空指针以及它是否会释放x指向的先前分配的空间。

这两个问题的答案要么是允许的,但 C 实现必须记录它所做的。

可能返回空或非空。

C 2018 7.22.3 1 说“……如果请求的空间大小为零,则行为是实现定义的:要么返回空指针以指示错误,要么行为就像大小是一些非零值,除非返回的指针不得用于访问对象。” 实现定义意味着实现必须记录它的作用,所以这应该在编译器和/或标准库文档中说明。

空间可能被释放,也可能不被释放。

关于使用 realloc 释放空间,C 2018 7.22.3.5 3 说“……如果 size 为零并且新对象没有分配内存,则旧对象是否由实现定义已解除分配……”

结论

要编写可移植代码,不要使用 realloc 来释放分配的所有空间。使用 free

要分配“零”空间,要么容忍 malloc 的任何返回值(null 或 not),要么总是分配至少一个字节,即使您不需要它。

,

realloc(ptr,0) 不等于 free(ptr)

引用标准(C18,§7.22.3.5 - IIRC,C11 没有改变):

The realloc function
Synopsis
1
#include <stdlib.h>
void *realloc(void *ptr,size_t size);

Description
2 The realloc function deallocates the old object pointed to by ptr and returns a pointer to a new
object that has the size specified by size. The contents of the new object shall be the same as that of
the old object prior to deallocation,up to the lesser of the new and old sizes. Any bytes in the new
object beyond the size of the old object have indeterminate values.
3 If ptr is a null pointer,the realloc function behaves like the malloc function for the specified size.
Otherwise,if ptr does not match a pointer earlier returned by a memory management function,or if
the space has been deallocated by a call to the free or realloc function,the behavior is undefined.
If size is nonzero and memory for the new object is not allocated,the old object is not deallocated.
If size is zero and memory for the new object is not allocated,it is implementation-defined whether
the old object is deallocated. If the old object is not deallocated,its value shall be unchanged.
 
4 The realloc function returns a pointer to the new object (which may have the same value as a
pointer to the old object),or a null pointer if the new object has not been allocated.

如您所见,它没有为大小为 0 的 realloc 调用指定特殊情况。它明确说明如果调用 realloc(ptr,0) 会发生什么,行为是实现定义的。

这几乎回答了您的问题:使用 realloc 代替免费不是您可以依赖的。

此外:assert(x != NULL) 是测试代码。每当分配内存时,你真的应该检查:

if (x == NULL) {}

您看到的行为恰好是 GCC 选择实现规范的方式,但不能依赖它。

link to C18 standard

,

TL;DR:不。不要使用 realloc(somePointer,0)

看起来 realloc(somePointer,0) 在不同的 C 标准之间发生了变化。来自 C89 [1]:

... 如果 ptr 是空指针,则 realloc 函数的行为 就像指定大小的 malloc 函数一样。 ...如果空间 无法分配,ptr 指向的对象不变。 如果 size 为零并且 ptr 不是空指针,它指向的对象是 获释。

但在较新的 C 标准中,情况不再如此。

来自 POSIX 手册页 [2]:

realloc() 的描述与之前的相比有所修改 本标准的版本与 ISO/IEC 9899:1999 保持一致 标准。以前的版本明确允许调用 realloc(p,0) 释放 p 指向的空间并返回空值 指针。虽然这种行为可以解释为允许 这个版本的标准,C 语言委员会有 表明这种解释是错误的。应用 应该假设如果 realloc() 返回空指针,则空间 p 指向的还没有被释放。由于这可能导致 双重释放,如果为空,实现也应该设置 errno 指针实际上表示失败,应用程序应该 仅当 errno 更改时才释放空间。

[1] http://port70.net/~nsz/c/c89/c89-draft.html#4.10.3.4

[2] https://pubs.opengroup.org/onlinepubs/9699919799/functions/realloc.html