【问题标题】:Using realloc to reduce the size of a memory block使用 realloc 减小内存块的大小
【发布时间】:2021-12-23 14:10:07
【问题描述】:

20 多年前,我对用 C 编写一些小程序有所了解,但即使在那个时候,我也可能并不是一直都在做正确的事情。现在我又想学C了,所以我真的是个新手。

根据这篇文章: Using realloc to shrink the allocated memory ,我做了这个测试,效果很好,但是让我很困扰:

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

int test (char *param) {
    char *s = malloc(strlen(param));
    strcpy(s, param);
    printf("original string    : [%4d] %s \n",  strlen(s), s);
    // reduce size
    char *tmp = realloc(s, 5);
    if (tmp == NULL) {
        printf("Failed\n");
        free(s);
        exit(1);
    } else {
        tmp[4] = 0;
    }
    s = tmp;
    printf("the reduced string : [%4d] %s\n", strlen(s), s );
    free(s);
}

void main(void){
    test("This is a string with a certain length!");
}
  1. 如果我遗漏了“tmp[4] = 0”,那么我仍然会取回整个字符串。这是否意味着字符串的其余部分仍在内存中,但不再分配?
  2. c 如何释放内存?它是自行跟踪内存还是由操作系统处理?
  3. 我释放了 s 字符串“free(s)”,是否还需要释放 tmp str(它确实指向同一个内存块,但它持有的(相同)地址可能存储在另一个内存位置?

这些很可能只是基础知识,但到目前为止我所阅读的内容都没有给我一个明确的答案(包括提到的文章)。

【问题讨论】:

  • char *s = malloc(strlen(param)); 太短 1 -- 回忆一下 '\0'(nul 终止字符)
  • realloc() 自动释放原始内存并返回指向重新分配内存的指针(除非新分配失败)。你不需要同时释放它们。
  • "c 怎么释放内存" -- 它依赖于实现,你不必担心。
  • 一些笔记。 1) main 必须返回一个 int。 2)test 永远不会返回任何东西,它应该是void test。 3) strlen 返回一个size_t。使用特殊的%zu 格式,而不是%d。 4) malloc + strcpy 有分配错误内存量的风险;你差了一个。请改用strdup。 5) 打开编译器警告。
  • 指针只是一个普通变量,它保存一个地址作为它的值。当您free() 内存时,您正在传递内存块的起始地址。当您执行s = tmp; 时,stmp 都拥有与其值相同的地址(例如,它们都指向内存中的相同地址)。你只需要释放一次(否则你会发现"double-free or corruption" 错误盯着你......)还要注意strdup() 是POSIX 不是标准C,所以你可能有也可能没有它。

标签: c


【解决方案1】:
  1. 如果我省略了“tmp[4] = 0”,那么我仍然会得到整个字符串。

您调用了未定义的行为。所有字符串操作都要求参数是以空字符结尾的字符数组。如果您减小分配的大小使其不包含空终止符,则当它试图找到它时,您正在访问分配之外。

这是否意味着字符串的其余部分仍在内存中,但不再分配?

实际上,当您缩小大小时,许多实现实际上并没有重新分配任何东西。他们只是简单地更新簿记信息,说分配的长度更短,并返回原始指针。因此,字符串的其余部分保持不变,除非您执行另一个恰好使用该内存的分配。

当您扩大规模时,甚至会发生这种情况。一些设计总是以特定粒度(例如 2 的幂)分配内存,因此如果您增加分配但不超过粒度,则不需要复制数据。

  1. c 如何释放内存?它是自行跟踪内存还是由操作系统处理?

堆管理是 C 运行时库的一部分。它可以使用多种策略。

  1. 我释放了 s 字符串“free(s)”,我是否还需要释放 tmp str(它确实指向同一个内存块,但它持有的(相同)地址可能存储在另一个内存位置?

s = tmp; 之后,stmp 都指向同一个分配的内存块。你只需要释放其中一个。

顺便说一句,初始分配应该是:

char *s = malloc(strlen(param)+1);

您需要为空终止符添加 1,因为 strlen() 不计算在内。

【讨论】:

    猜你喜欢
    • 2016-07-04
    • 2017-06-15
    • 1970-01-01
    • 2016-05-29
    • 2023-03-25
    • 2019-07-20
    • 1970-01-01
    • 2014-04-04
    • 2021-10-31
    相关资源
    最近更新 更多