我有这样的代码:
void **array = (void**)malloc(sizeof(void*)*4);
array[0] = (void *)"Hello";
array[1] = (void *)"World";
array[2] = (void *)"My";
array[3] = (void *)"Example";
array = (void **)realloc(array,sizeof(void*)*3);
printf("%s\n",(char*)array[3]);
free(array);
即使我重新分配内存,它也会打印Example
我哪里错了?
答案 0 :(得分:3)
它有效,因为有时未定义的行为实际上 有效。这仍然不是一个好主意,因为它不是保证工作。
有可能发生的事情是realloc
电话根本没有改变任何事情,因为你已经要求减少一个已经微不足道的小块。
许多内存分配函数具有一定的分辨率,例如总是十六个字节的倍数。因此,在这种情况下,无论您是要求三个四字节指针还是四个四字节指针,您总是会获得一个16字节的块(加上开销)。这也意味着,如果你告诉它将你的16字节块减少到12字节的块,那么它可能足够聪明,可以不管它。
您可以通过在realloc
之前和之后打印指针来检查这一点 - 它可能是完全相同的值。
所以它只留下了内存,这就是为什么当你访问数组的第四个元素时,它仍然像以前一样设置。
但是,如上所述,这是一个不保证的实现细节,因此您不应该依赖它。 永远!的
答案 1 :(得分:1)
见:
$ cat test.cpp
#include <stdlib.h>
#include <stdio.h>
int main() {
void **array = (void**)malloc(sizeof(void*)*4);
array[0] = (void *)"Hello";
array[1] = (void *)"World";
array[2] = (void *)"My";
array[3] = (void *)"Example";
array = (void **)realloc(array,sizeof(void*)*3);
printf("%s\n",(char*)array[3]);
free(array);
}
$ g++ test.cpp -o a -fsanitize=address
$ ./a
=================================================================
==11051== ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60060000efc8 at pc 0x400a3f bp 0x7fffafc0bf40 sp 0x7fffafc0bf38
READ of size 8 at 0x60060000efc8 thread T0
#0 0x400a3e (/tmp/a+0x400a3e)
#1 0x7fe6d321aec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4)
#2 0x400868 (/tmp/a+0x400868)
0x60060000efc8 is located 0 bytes to the right of 24-byte region [0x60060000efb0,0x60060000efc8)
allocated by thread T0 here:
#0 0x7fe6d35d355f (/usr/lib/x86_64-linux-gnu/libasan.so.0.0.0+0x1555f)
#1 0x400a12 (/tmp/a+0x400a12)
#2 0x7fe6d321aec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4)
Shadow bytes around the buggy address:
0x0c013fff9da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c013fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c013fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c013fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c013fff9de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c013fff9df0: fa fa fa fa fa fa 00 00 00[fa]fa fa fd fd fd fd
0x0c013fff9e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c013fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c013fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c013fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c013fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Heap righ redzone: fb
Freed Heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
ASan internal: fe
==11051== ABORTING
所以,实际上释放了这些内存字节,但是你很幸运(或不是)这些字节没有被重用用于其他任何内容。
realloc 调用会更改已分配内存的大小。在您的情况下,您降低了一个大小,因此系统只是切断了之前分配的连续区域中的字节。即你的文本指针仍然存在,只是系统还没有将这些字节重用于另一件事。在我的示例中,我使用调试选项-fsanitize=address
编译您的代码,该选项检查缓冲区溢出和释放内存的使用情况,并在发生类似情况时终止应用程序并显示错误消息 - 并且它会发生在您的情况下。
答案 2 :(得分:1)
试试这个:
printf( "before: %p\n", array );
array = (void **)realloc(array,sizeof(void*)*3);
printf( "after: %p\n", array );
可能发生的是您正在使用通知的堆实现,您只是从分配中删除了几个字节,并且除了标记当前块之外没有做任何其他事情在返回相同指针之前更小。
我猜测你在x86 CPU上运行32位应用程序。所以你的指针大概是4个字节,但x86有一些数据类型有8字节的对齐限制。这很重要,因为C standard的7.20.3说:
连续调用分配的存储顺序和连续性 calloc,malloc和realloc函数未指定。指针 如果分配成功,则返回适当的对齐,以便它可以 被分配给指向任何类型对象的指针,然后用于访问 分配空间中的此类对象或此类对象的数组......
这意味着在实践中,x86机器上的大多数malloc()/calloc()/realloc()/free()
实现以8字节块的形式返回内存。由于你的块从16个字节开始 - 或者是两个8字节的块 - 并且你把它缩短为12个 - 这仍然是两个8字节的块 - 你的realloc()
调用没有移动内存。
换句话说,未定义的行为。如果您从数组中的2000个指针开始并且realloc()
指向两个指针,那么不要期望它能够工作。