realloc会对旧指针做什么?

5

关于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

指针仍然指向相同的地址,但内容已经改变。


4
阅读realloc()规范以及Linux手册或任何手册。行为并不完全一致,它取决于情况。此外,不要像那样编写没有int返回类型的main(),那真的很老旧和不建议使用 [标签:c]。 - Iharob Al Asimi
4
C严格按值传递!realloc怎么可能改变指针? - too honest for this site
2
@iharob:在哪个方面不一致?C规范对其行为非常明确。 - too honest for this site
2
@Olaf 如果可能的话,它可能会返回一个不同的指针或相同的指针,这就是我的意思。我错了吗? - Iharob Al Asimi
它在某种意义上是不一致的,有时是这样,另一次是那样。作为“行为”,它当然符合规范。 - Iharob Al Asimi
显示剩余2条评论
6个回答

11

指针a仍然指向相同的地址,但内容已更改。

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


由于在realloc释放旧内存之前必须分配指向不同块的新指针(数据必须被复制),因此OP示例可能仍然能够访问先前的数据,但这是未定义的行为 - Weather Vane

6
realloc函数返回的值告诉您它是成功还是失败。
b = realloc(a, 200000 * sizeof(int));

如果realloc函数调用失败,它将返回一个空指针,并且a仍然指向未修改的原始内存块(当然b也是一个空指针)。
如果调用成功,那么b指向一个(可能是新分配的)内存块,而a的值是不确定的。如果能够在原地增加或缩小块的情况下分配新块,则b将等于a--但测试这一点,甚至引用a的值都具有未定义的行为。如果必须重新定位块,则realloc函数将在复制数据后执行等效于free(a)的操作。无论哪种情况,最好将a设置为NULL,以避免意外引用其(现在不确定的)值。
请注意,realloc函数可以重定位内存块,即使新大小更小也是如此。

Realloc函数如果成功,肯定会释放原始指针吗?你有相关文档吗?我相信你,但是如果有人问起,我想要有证据。 - velocirabbit

1

一个简单的 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;
}
< p > malloc和相关函数并不总是准确地分配所需的大小。很多时候它们分配比所需大小更多的空间。内存分配函数会记录一些隐藏数据,允许通过由malloc或相关函数分配的指针查找已分配的内存块大小。如何记录这些数据并不需要理解,但有些非常简单的实现只是将大小存储在返回的指针之前的空间中*(((size_t)ptr)-1)


1
我曾以为 realloc 也可以缩小内存。 - Weather Vane
1
也许这篇Stack Overflow的帖子可以为本主题做出贡献 - 链接在这里 - user3078414
@WeatherVane:它可以,但在这个简化版本中,实际分配的内存块大小永远不会减少。在此代码中尝试这样做将使其变得更加冗长,并且开始过度依赖内存分配实现。如果这对问题不够满意,请谅解。 - nategoose

0
如果realloc()返回一个与您传入的指针不同的指针(大多数情况下都是这样),那么您传入的指针就不再属于您,您也不需要知道或关心它的去向。它可能会更改其内容,也可能不会。但是您不再被允许访问它,因此它不应该是您关心的问题。

0

阅读 man 手册在这里是关键,但 TLDR 是如果没有足够的内存来扩大前一个块的后端,它将获得一个新的内存块,将旧数据复制到其中,并返回新块的地址。旧地址不应使用,大多数典型的 realloc 语句看起来像这样

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

这样你就不会意外地使用可能错误的旧值。

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

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

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

2
这与通常的建议相违背,即将新指针分配给不同的变量。然后,如果它是NULL,则先前的指针仍然有效,并且任何不仅在该点上退出的严肃应用程序都可以实现恢复策略-例如,将您真正不想丢失的重要数据文件化。如果分配良好,则替换原始指针。 - Weather Vane

0
如果'a'指向一个有效的内存块(来自先前的malloc/realloc/calloc),那么realloc调用将尝试提供您请求的新大小的内存块。 realloc调用应该是这样的形式:*tmp = realloc(a ... 必须测试realloc的返回值。如果它为NULL,则realloc无法分配所请求的内存,这将使'a'成为有效指针。然后,您需要负责处理由'a'指向的任何数据(保存/丢弃它),并负责释放'a'指向的内存。
如果realloc调用成功,请使b = tmp,现在'b'是指向内存块的新指针 - 开始位置是否与'a'相同并不重要。'a'不再是有效的内存分配指针,尽管进一步的错误取决于'a'是否指向您的程序拥有的内存 - 基本上,如果a == b,则可以访问'a'而不会出现明显的错误。

在有效的*tmp = realloc(a ... & b = tmp;之后:
1)如果重新分配内存的起始位置未更改:(a == b)
它将分配请求的内存
但在valgrind下运行,您会看到错误消息:
Invalid free() / delete / delete[] / realloc()
Address 0x51fc040 is 0 bytes inside a block of size 256 free'd
在这种情况下,realloc无法释放'a'指向的内存
并且再次在这种情况下,'a'仍然可以被访问,因为它是分配给您的程序的内存的指针

2)如果重新分配内存的起始位置已更改:(a != b)
它将失败,Valgrind显示以下输出:
a的地址:0x1e89010
b的地址:0x7f2c5893c010
realloc后的a: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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接