为什么 C 中的 realloc() 会改变内存?

Why realloc() in C changes memory?

我认为 realloc() 函数会重新分配内存并且不会更改它。 在调试了一个我必须做的任务后,我意识到 realloc() 改变了内存中的一些元素并产生了错误。

所以我写了下面的代码:

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

int main() {
    int *a, *p;
    a = (int*)calloc(10, sizeof(int));
    for (int i = 0;i < 10; i++)
        a[i] = i;
    for (int i = 0; i < 10; i++)
        printf("%d ", a[i]);
    printf("\n");
    p = realloc(a, 20);
    p[10] = 10;
    for (int i = 0; i < 11; i++)
        printf("%d ", p[i]);
    printf("\n");
    for (int i = 0; i < 10; i++)
        printf("%d ", a[i]);

    return 0;
}

我得到了以下输出

谁能解释为什么会这样?

我尝试使用 malloc() 而不是 calloc(),但现有数组也会更改。

发生这种情况是因为您重新分配的项目 比最初分配的 calloc。造成混淆的原因是 callocmalloc/realloc 的签名略有不同。来电

a = calloc(10, sizeof(int));

分配 10 个 sizeof(int) 块,或者在 sizeof(int) 等于 4 的系统上分配 40 个字节,而

p = realloc(a, 20);

要求将该块的大小减小到 20 个字节。换句话说,calloc 为你做乘法 sizeof,而 reallocmalloc 需要你自己做乘法。

一旦您告诉 realloc 您只需要 20 个字节,任何超过第五个整数末尾的访问都是未定义的行为。

如果要将大小扩展到 20 ints,乘以 sizeof(int):

p = realloc(a, 20*sizeof(int));

p = realloc(a, 20);

在此语句中,您将 p 重新分配为 20/4 = 5。

访问未定义的内存导致undefined behavior。这意味着编译器可以以任何方式运行:程序崩溃、垃圾值、有时是正确的结果。

这就是为什么您在第 5 个元素之后得到垃圾值的原因。

编辑为:p = realloc(a, 20*sizeof(int));

来自 cppreference

The reallocation is done by either:

a) expanding or contracting the existing area pointed to by ptr, if possible. The contents of the area remain unchanged up to the lesser of the new and old sizes. If the area is expanded, the contents of the new part of the array are undefined.

b) allocating a new memory block of size new_size bytes, copying memory area with size equal the lesser of the new and the old sizes, and freeing the old block.

这取决于编译器的实现和您重新分配的大小。如果新分配的size在没有fragment的情况下是不可占用的,那么realloc肯定会在不同的位置分配内存,这比释放已经用完的内存效率更高。

语句 a = (int*)calloc(10, sizeof(int)); 分配了 10x440 字节。假设从 0x1000x140 位置。

    -----------------------------------------
   |     |    |    |   |    |    |      |    |
    -----------------------------------------
   0x100     ..      0x120                0x140
   a

下一步,当你像 p = realloc(a, 20); 一样对 a 执行 realloc() 时,它不会添加 20 字节,它会将整个内存减少到 20 字节本身,即从 0x1000x120,现在当您访问 p[10] = 10; 时,它超出了范围,即您试图将数据放在 0x140 附近的位置,这会导致未定义的行为。

    --------------------
   |     |      |      | 
    --------------------
   0x100     ..      0x120 
   a                   
   p

来自 man 3 realloc

The realloc() function changes the size of the memory block pointed to by ptr to size bytes. The contents will be unchanged in the range from the start of the region up to the minimum of the old and new sizes. If the new size is larger than the old size, the added memory will not be initialized.

如果您真的想将现有内存扩展 20 字节,那么请执行

p = realloc(a ,20*sizeof(int));