【问题标题】:what will realloc do to the old pointer [duplicate]realloc 会对旧指针做什么
【发布时间】:2016-08-23 17:34:52
【问题描述】:

我对 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仍然指向同一个地址,只是内容发生了变化。

【问题讨论】:

  • 阅读realloc() 规范,也许还有Linux 手册或任何手册。行为并不是真正一致的,这取决于。另外,不要像没有int 返回类型的情况下那样做main(),那真的很旧并且不推荐使用c
  • C 是严格按值传递的! realloc 怎么能改变指针?
  • @iharob:我哪一种感觉不一致? C 规范对行为非常清楚。
  • @Olaf 如果可能的话,它可以返回不同的指针或相同的指针,这就是我的意思。我错了吗?
  • @iharob:不,你不是,但那是标准中定义的行为,所以不是不一致,而是正常的。 fread 可能返回的数据比请求的少,等等。

标签: c pointers realloc


【解决方案1】:

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

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

【讨论】:

  • 并且由于必须在realloc释放旧内存之前分配指向不同块的新指针(必须复制数据),因此OP示例很可能仍然能够访问以前的数据- 但那是未定义的行为
【解决方案2】:

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

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

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

如果成功,则b 指向(可能是新分配的)内存块,a 的值不确定。如果它能够将新块分配在与旧块相同的位置(通过就地增加或缩小块),那么b 将等于a——但要测试它,甚至参考a 的值具有未定义的行为。如果它必须重新定位块,那么realloc 将在复制数据后完成与free(a) 等效的操作。无论哪种情况,最好将 a 设置为 NULL 以避免意外引用其(现在不确定的)值。

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

【讨论】:

  • Realloc 成功后肯定会释放原始指针?你有这方面的文件吗?我相信你,但我想有证据以防有人问。
【解决方案3】:

一个简单的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) 之前的空间中。

【讨论】:

  • 我认为realloc 也可以缩小内存。
  • 也许这篇 SO 帖子可以为这个话题做出贡献 - here's the link
  • @WeatherVane:可以,但在这个简化版本中,实际分配的内存块大小从未减少。试图在这段代码中这样做会使它变得不那么简洁,并且会开始过于依赖内存分配实现。如果这对这个问题还不够,我很抱歉。
【解决方案4】:

如果realloc() 返回的指针与您传入的指针不同(大多数情况下会这样),那么您传入的指针不再属于您,您无权知道或关心会发生什么它。它可能会更改其内容,也可能不会。但是您不再被允许访问它,所以它与您无关。

【讨论】:

    【解决方案5】:

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

    必须测试 realloc 的返回值
    如果它为 NULL,则 realloc 无法分配请求的内存,这会将 'a' 作为有效指针
    然后,您负责处理 'a' 指向的任何数据(保存/丢弃),并且您负责 free 处理 'a' 指向的内存

    如果 realloc 调用成功,则生成 b = tmp 并且现在 'b' 是指向内存块的新指针 - 起始位置是否与 'a' 相同或不同都无关紧要。 '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 显示如下输出:
    地址: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;
    }
    

    【讨论】:

      【解决方案6】:

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

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

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

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

      编辑:根据天气风向标的绝对正确的评论,更安全的路线是

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

      【讨论】:

      • 这违反了通常的建议,即将新指针分配给不同的变量。那么如果是NULL,那么前一个指针仍然有效,并且此时任何不只是exit 的严肃应用程序都可以实施恢复策略 - 例如归档您真的不想丢失的重要数据.如果分配良好,则替换原来的指针。
      猜你喜欢
      • 2015-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-28
      • 2016-05-26
      • 1970-01-01
      • 2017-10-21
      • 2010-09-17
      相关资源
      最近更新 更多