realloc 将对旧指针做什么

what will realloc do to the old pointer

我对 realloc 函数有疑问。应用realloc函数后旧指针的内容会改变吗? 密码是

main () {
    int *a, *b, i;

    a = calloc(5, sizeof(int));
    for (i = 0; i < 5; i++)
            a[i] = 1;
    for (i = 0; i < 5; i++)
            printf("%d", a[i]);
    printf("\n%p\n", a);

    b = realloc(a, 200000 * sizeof(int));
    if(b == NULL)
            printf("error\n");
    for (i = 0; i < 5; i++)
            printf("%d", a[i]);
    printf("\n");
    for (i = 0; i < 10; i++)
            printf("%d", b[i]);

    printf("\n%p %p\n", a, b);
}

输出为

11111
0x2558010
00111
1111100000
0x2558010 0x7f29627e6010

指针a还是指向同一个地址,只是内容变了。

阅读手册页是这里的关键,但是TLDR是如果在前一个块的后端没有足够的内存来放大,它会得到一个新的内存块,将旧数据复制到其中, 和 return 新块的地址。不应使用旧地址,最典型的 realloc 语句如下所示

   a = realloc(a, 200000 * sizeof(int));

这样你就不会不小心使用了可能错误的旧值。

它不能改变指针中的地址,因为它是按值传递的,所以在函数中改变它只是改变本地副本。

编辑:根据 Weather Vane 的绝对正确评论,更安全的路线是

   void * b = realloc(a, 200000 * sizeof(int));
   if ( b ) {
       a = b;
   } else {
       ;;; /* error handler here */
   }

Pointer a still point to the same address, but the content is changed.

那是因为 realloc() 可能首先尝试增加 a 指向的块的大小。但是,它可以改为分配一个新块,将数据(或尽可能多的数据)复制到新块,然后释放旧块。你真的不应该在调用 b = realloc(a, 200000 * sizeof(int)) 之后使用 a 因为 realloc 调用可能会将块移动到新位置,使 a 指向不再分配的内存。请改用 b

一个简单的 realloc 实现应该可以回答您的问题:

void * realloc(void * ptr, size_t desired_size) {
    size_t allocated_size = _allocated_size_of(ptr);
    if (allocated_size < desired_size) {
        void * new_ptr = malloc(desired_size);
        memcpy(new_ptr, ptr, allocated_size);
        free(ptr);
        ptr = new_ptr;
    }
    return ptr;
}

malloc 和相关函数并不总是准确分配所需的大小。他们经常分配超过所需的大小。内存分配函数保留了一些隐藏数据,允许使用 malloc 或相关函数分配的指针来查找分配的内存块大小。这是如何跟上的并不需要理解,但一些非常简单的实现只是在指针返回 *(((size_t)ptr)-1).

之前将大小存储在 space 中

如果realloc() returns一个指针与你传入的指针不同(大多数时候会这样),那么你传入的指针不再属于你,你有没有企业知道或关心它会变成什么样子。它可能会更改其内容,也可能不会。但是你不再被允许访问它,所以它与你无关。

realloc返回的值告诉你它是成功还是失败。

b = realloc(a, 200000 * sizeof(int));

如果失败,它returns一个空指针,a仍然指向原始未修改的内存块(当然b是一个空指针)。

如果成功,则b指向一个(可能是新分配的)内存块,a的值为不确定。如果它能够在与旧块相同的位置分配新块(通过在适当的位置增加或缩小块),那么 b 将等于 a - 但测试它,或者即使引用 a 的值,也有未定义的行为。如果它必须重新定位块,那么 realloc 将在复制数据后完成相当于 free(a) 的操作。在任何一种情况下,最好将 a 设置为 NULL 以避免意外引用其(现在不确定的)值。

请注意 realloc 可以重新定位块,即使新大小更小。

如果 'a' 指向一个有效的内存块(来自之前的 malloc/realloc/calloc),则 realloc 调用将尝试提供一个具有您请求的新大小的内存块
realloc 调用的格式应为 *tmp = realloc (a ...

必须测试来自 realloc 的 return 值
如果为 NULL,则 realloc 无法分配请求的内存,这使得 'a' 成为有效指针
然后您负责处理 'a' 指向的任何数据(保存/丢弃)并且您负责 free ing 'a'

指向的内存

如果 realloc 调用成功 make b = tmp 现在 'b' 是指向内存块的新指针 - 起始位置是否与 [=49= 相同并不重要] 或不同。 'a' 不再是有效的内存分配指针,尽管进一步的错误将取决于 'a' 是否指向您的程序拥有的内存 - 基本上如果 a == b,'a' 可以是访问没有明显错误。

经过有效的 *tmp = realloc(a ... & b = tmp;:
1) 如果重新分配内存的起始位置不变:(a == b)
它将分配请求的内存
但是 运行 它在 valgrind 下你会看到错误信息:
无效的 free() / delete / delete[] / realloc()
地址 0x51fc040 是大小为 256 的块内的 0 字节 free'd
在这种情况下,realloc 无法释放 'a'
指向的内存 在这种情况下 'a' 仍然可以访问,因为它是指向分配给您的程序的内存的指针

2) 如果重新分配内存的起始位置发生改变:(a != b)
它会失败并且 Valgrind 显示如下输出:
地址a:0x1e89010
b地址:0x7f2c5893c010
重新分配后:0x1e89010
`./test15' 中的错误:realloc():旧大小无效:0x0000000001e89010

并尝试访问 'a' 将失败 - 即使尝试将其值作为指针打印也会失败,大概是因为它不再指向程序拥有的内存

换句话说,在 b = realloc(a ... 之后使用 'a' 是未定义的行为。
以上评论基于使用以下代码:

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

int main(void)
{
    int *a = NULL, *b = NULL, *c = NULL;

    /* initial allocation */
    a = malloc(256);
    if( a == NULL) return (1);
    printf("address of a: %p\n", a);

    /* reallocation 'b' MAY be same as 'a' - try much larger allocations */
    void *tmp = realloc(a, 512);
    if ( !tmp ) {
        free(a);
        return (1);
    } else {
        b = tmp;
    }
    printf("address of b: %p\n", b);

    /* see what 'a' is now - this MAY crash the program*/
    printf("a after realloc: %p\n", a);

    /* 'a' may not be a valid pointer - try using it for another realloc */
    c = realloc(a, 256);
    /* Valgrind shows that memory could not be free'd or 'a' was not valid allocated memory */
    printf("return value of c: %p\n", c);
    if (c != NULL) {
        free(c);
        printf("'c' allocated\n");
    } else {
        free(b);
        printf("'c' not allocated\n");
    }

    return 0;
}