realloc 行为的可移植性
Portability of realloc behavior
我一直在编写在许多循环中分配数据的代码,发现使用 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, 0): %p\n", x);
x = 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, 0): %p\n", 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)
在这种情况下,它意外地产生了一个非 NULL 地址。
我可以在多大程度上依赖这种行为?
编辑:准确地说,我的问题是我是否可以依靠我的示例程序在任何平台上都没有 UB 或内存泄漏。
谢谢。
这是 C11 标准(更准确地说,它是一个草案,但它是最接近公开可用标准的东西)所说的:
Synopsis
#include <stdlib.h>
void *realloc(void *ptr, size_t size);
Description
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.
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 memory for the new object cannot be allocated, the old object is not deallocated and its value is unchanged.
Returns
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 could not be allocated.
https://port70.net/~nsz/c/c11/n1570.html#7.22.3.4
因此 realloc (NULL, 0)
与 malloc(0)
相同,后者可能 return 一个不能被取消引用的非空指针。
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
在询问“此行为”时,您的问题并未具体说明您对哪种行为感兴趣。据推测,您感兴趣的是 realloc
是否可以 return 空指针对于 realloc(x, 0)
以及它是否会释放先前分配的 space 指向的 x
.
这两个问题的答案都是允许的,但 C 实现必须记录它所做的。
可以 return 编辑空值或非空值。
C 2018 7.22.3 1 说“......如果请求的 space 的大小为零,则行为是实现定义的:空指针是 returned 以指示错误,或者行为就好像大小是某个非零值,除了 returned 指针不得用于访问对象。” 实现定义的意味着实现必须记录它所做的事情,所以这应该在编译器and/or标准库文档中说明。
Space可能会被释放,也可能不会被释放。
关于使用realloc
释放space,C 2018 7.22.3.5 3说“......如果size
为零并且没有为新对象分配内存,它是实现-定义旧对象是否被释放……”
结论
要编写可移植代码,请不要使用 realloc
释放分配的所有 space。使用 free
.
要分配“零”space,要么容忍来自 malloc 的任何 return 值(无论是否为空),要么始终分配至少一个字节,即使您不需要它。
TL;DR:不。不要使用 realloc(somePointer,0)
。
看起来 realloc(somePointer,0)
在不同的 C 标准之间发生了变化。来自 C89 [1]:
... If ptr is a null pointer, the realloc function behaves
like the malloc function for the specified size. ... If the space
cannot be allocated, the object pointed to by ptr is unchanged. If
size is zero and ptr is not a null pointer, the object it points to is
freed.
但在较新的 C 标准中不再是这种情况。
来自 POSIX 手册页 [2]:
The description of realloc() has been modified from previous
versions of this standard to align with the ISO/IEC 9899:1999
standard. Previous versions explicitly permitted a call to
realloc(p, 0) to free the space pointed to by p and return a null
pointer. While this behavior could be interpreted as permitted by
this version of the standard, the C language committee have
indicated that this interpretation is incorrect. Applications
should assume that if realloc() returns a null pointer, the space
pointed to by p has not been freed. Since this could lead to
double-frees, implementations should also set errno if a null
pointer actually indicates a failure, and applications should
only free the space if errno was changed.
[1] http://port70.net/~nsz/c/c89/c89-draft.html#4.10.3.4
[2]https://pubs.opengroup.org/onlinepubs/9699919799/functions/realloc.html
我一直在编写在许多循环中分配数据的代码,发现使用 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, 0): %p\n", x);
x = 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, 0): %p\n", 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)
在这种情况下,它意外地产生了一个非 NULL 地址。
我可以在多大程度上依赖这种行为?
编辑:准确地说,我的问题是我是否可以依靠我的示例程序在任何平台上都没有 UB 或内存泄漏。
谢谢。
这是 C11 标准(更准确地说,它是一个草案,但它是最接近公开可用标准的东西)所说的:
Synopsis
#include <stdlib.h> void *realloc(void *ptr, size_t size);
Description
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.
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 memory for the new object cannot be allocated, the old object is not deallocated and its value is unchanged.
Returns
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 could not be allocated.
https://port70.net/~nsz/c/c11/n1570.html#7.22.3.4
因此 realloc (NULL, 0)
与 malloc(0)
相同,后者可能 return 一个不能被取消引用的非空指针。
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
在询问“此行为”时,您的问题并未具体说明您对哪种行为感兴趣。据推测,您感兴趣的是 realloc
是否可以 return 空指针对于 realloc(x, 0)
以及它是否会释放先前分配的 space 指向的 x
.
这两个问题的答案都是允许的,但 C 实现必须记录它所做的。
可以 return 编辑空值或非空值。
C 2018 7.22.3 1 说“......如果请求的 space 的大小为零,则行为是实现定义的:空指针是 returned 以指示错误,或者行为就好像大小是某个非零值,除了 returned 指针不得用于访问对象。” 实现定义的意味着实现必须记录它所做的事情,所以这应该在编译器and/or标准库文档中说明。
Space可能会被释放,也可能不会被释放。
关于使用realloc
释放space,C 2018 7.22.3.5 3说“......如果size
为零并且没有为新对象分配内存,它是实现-定义旧对象是否被释放……”
结论
要编写可移植代码,请不要使用 realloc
释放分配的所有 space。使用 free
.
要分配“零”space,要么容忍来自 malloc 的任何 return 值(无论是否为空),要么始终分配至少一个字节,即使您不需要它。
TL;DR:不。不要使用 realloc(somePointer,0)
。
看起来 realloc(somePointer,0)
在不同的 C 标准之间发生了变化。来自 C89 [1]:
... If ptr is a null pointer, the realloc function behaves like the malloc function for the specified size. ... If the space cannot be allocated, the object pointed to by ptr is unchanged. If size is zero and ptr is not a null pointer, the object it points to is freed.
但在较新的 C 标准中不再是这种情况。
来自 POSIX 手册页 [2]:
The description of realloc() has been modified from previous versions of this standard to align with the ISO/IEC 9899:1999 standard. Previous versions explicitly permitted a call to realloc(p, 0) to free the space pointed to by p and return a null pointer. While this behavior could be interpreted as permitted by this version of the standard, the C language committee have indicated that this interpretation is incorrect. Applications should assume that if realloc() returns a null pointer, the space pointed to by p has not been freed. Since this could lead to double-frees, implementations should also set errno if a null pointer actually indicates a failure, and applications should only free the space if errno was changed.
[1] http://port70.net/~nsz/c/c89/c89-draft.html#4.10.3.4
[2]https://pubs.opengroup.org/onlinepubs/9699919799/functions/realloc.html