用C语言与Realloc混淆



我有这样的代码:

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("%sn",(char*)array[3]);
free(array);

即使我重新分配内存,它正在打印Example

我在哪里错了?

它起作用,因为有时不确定的行为实际上是工作。这仍然不是一个好主意,因为它不能保证工作。

封面下可能发生的可能发生的是realloc调用根本没有更改任何内容,因为您已经要求在一个已经很小的块中减少少量。

许多内存分配函数具有一定的分辨率,它们分配给了16个字节的倍数。因此,在这种情况下,无论您是要求三个字节指针还是四个四字节指针,您始终会得到16个字节的块(加上开销)。这也意味着,如果您告诉它将您的16个字节块减少到十二个字节块,那么只能不理就足够聪明。

您可以通过在realloc之前和之后打印出指针来检查一下 - 它可能是完全相同的值。

因此,它只是单独离开内存,这就是为什么当您访问数组的第四个元素时,它仍然像以前一样设置。

但是,如前所述,这是一个实现细节,不保证,因此您不应依靠它。 ever!

请参阅此:

$ 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("%sn",(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 调用更改分配的内存的大小。在您的情况下,您降低了尺寸,因此系统只需切断以前分配的连续区域中的字节即可。IE。您的文本指针仍然存在,只是系统尚未将这些字节重复使用。在我的示例中,我将您的代码与DEBUG选项-fsanitize=address编译为检查缓冲区溢出和释放内存的使用情况,并在您的情况下终止带有错误消息的应用程序。

尝试以下:

printf( "before: %pn", array );
array = (void **)realloc(array,sizeof(void*)*3);
printf( "after: %pn", array );

可能发生的是您正在使用通知的堆实现,您只是从分配中删除了几个字节,除了在返回相同的指针之前对当前块标记稍小一点。

我猜您正在X86 CPU上运行32位应用程序。因此,您的指针可能是4个字节,但是X86具有一些具有8字节对齐限制的数据类型。这很重要,因为C标准的7.20.3说:

通过连续调用对 calloc,malloc和realloc函数未指定。指针 如果分配成功是适当对齐的,则返回 被分配给指向任何类型对象的指针,然后用于访问 这样的对象或分配空间中的此类对象数组...

这意味着在实践中,X86机器上的大多数malloc()/calloc()/realloc()/free()实现都在8字节块中返回存储器。由于您的块从16个字节开始(或两个八字节块),然后将其缩短到12个字节 - 它仍然是两个八字节的块 - 您的realloc()呼叫没有移动内存。

换句话说,未定义的行为。如果您从数组中的2000个指针开始,并且您的realloc()降至两个指针。

相关内容

  • 没有找到相关文章

最新更新