【问题标题】:Do I have to free dynamic allocated array after returning it from a function to another?将动态分配的数组从一个函数返回到另一个函数后,我是否必须释放它?
【发布时间】:2017-07-17 04:57:26
【问题描述】:

考虑以下函数:

char* color(const char* clr, char* str)
{
    char *output = malloc(strlen(str)+1);

    /* Colorize string. */
    sprintf(output, "%s%s%s", clr, str, CLR_RESET);

    return output;
}

上述函数允许我们在 linux 终端打印彩色消息。

所以我可以写

printf("%s", color(CLR_RED, "This is our own colorized string"));

并查看红色消息This is our own colorized string

我关心的是color() 中分配的output 字符串。当函数返回一个分配的元素(在我们的例子中是数组)时,我们有点需要释放它(C 没有垃圾收集器)。

我的问题是,output 在传递给printf() 之后会发生什么?当我们退出函数printf() 时,数组仍然被分配?如果是,我们如何解除分配?有什么方法可以为我们编写一个函数吗?

提前致谢!

【问题讨论】:

  • 规则很简单:对于malloc(...)/calloc(...)/realloc(NULL, ...) /strdup(...) 返回的每个值,都需要对free() 进行一次调用,传递这个值。
  • @alk 谢谢。我只是想确定一下。
  • 你也可以 printf("%s", CLR_RED "This is..." CLR_RESET);.字符串被连接起来。
  • @linuxfan 不错。
  • 这仅在CLR_RED 是(某些宏扩展为)文字字符串时才有效

标签: c arrays


【解决方案1】:

另一种方法是使用全局缓冲区 char[whatever]:不需要 alloc(),不需要 free()。 color() 函数总是返回那个缓冲区。唯一可能不是问题的问题是 color() 将是可重入的,并且每个语句只能调用一次。

char g_color_buf[200];

char* color(const char* clr, char* str) {
    sprintf(g_color_buf, "%s%s%s", clr, str, CLR_RESET);
    return g_golor_buf;
}

更新:从下面的评论来看,最好在函数本身内将 g_color_buf[] 声明为 static,而不是污染全局命名空间。这样做并不能解决其他问题,但无论如何都会更好。

如上所述,此例程不应用于以下语句:

printf("%s %s", color(CL_WHITE, "Result:"), color(CL_RED, "Error"));

因为对 color() 的第二次调用会破坏第一次调用所做的工作。

最后,为了回答您的问题(需要,因为否则有人会说“您没有回答”),您的示例代码中由 color() 分配的内存将保留在那里,直到 (没有其他人)将释放它。考虑到您可能想要的平均正常使用,写很多次会很不舒服:

tmp = color(CL_RED, "hello");
printf("%s", tmp);
free(tmp);

使用全局缓冲区传递给 color() 的解决方案,可能还伴随着它的大小,例如:

printf("%s", color(buffer1, CL_RED, "Hello"));
--or--
printf("%s", color(buffer2, sizeof(buffer2), CL_RED, "HelloHello"));

当然是最正确的,但很无聊。好吧,如果你喜欢打字,那就不无聊了!

【讨论】:

  • 在您撰写之前,该内容已包含在最新版的 Georgi's answer 中。
  • @BasileStarynkevitch。不。读得好。我说的是一个全局缓冲区,它没有作为参数传递给函数。与 Giorgi 的回答完全不同,调用更简单,并且不可重入
  • 为什么是全球性的?为什么不是static 函数本地?此外,重入的问题不在于“您必须在每个语句中调用一次”:而是在任何情况下都不可能有多个站点以潜在的交错方式调用它,否则会发生 UB。也不是“第二次调用 color() 会破坏第一次调用所做的工作”;为什么会有问题?真正的问题是这两个调用的顺序是未定义的(因为它们是函数参数),所以它们根本不需要顺序发生(非交错),更不用说按写入的顺序了
  • @underscore_d:我更新了关于静态本地的答案。好点子。关于 printf() 中 color() 的双重调用:这是一个问题,因为最后 printf 收到了两个指向同一个缓冲区的指针。显然,这个缓冲区不能同时保存两个不同的内容,所以 printf() 会打印两次相同的字符串,这不是我们想要的结果。
  • @EJP。你说的对。但是 printf() 都不能保证是线程安全的。 OP 谈论的是 C,而不是 Posix,他的例子似乎是以 printf() 为中心的。不过,我可以想象这个 color() 函数可以在不同的上下文中使用,而不是打印到控制台。
【解决方案2】:

首先,您的代码是错误的。你打电话给malloc,尺寸太小,所以会有buffer overflow。你忘了测试malloc 的失败。您可能应该编码:

char *output = malloc(strlen(clr)+strlen(str)+strlen(CLR_RESET)+1);
if (!output) { perror ("malloc in color"); exit(EXIT_FAILURE); }
sprintf(output, "%s%s%s", clr, str, CLR_RESET);

顺便说一句,有些系统有asprintfuse 会更容易)。恕我直言,使用sprintf 很危险(您应该更喜欢snprintf)。

那么,C 编程需要很多约定。你应该有你的(受惯常做法的启发)。我建议研究现有的free software 源代码(例如来自github)以获得灵感。

您可以让函数返回一个malloc-ed 指针,但您需要记录该约定(至少,作为公共头文件中的注释声明 color) 并明确有义务在其结果上调用 free,并在其他地方遵循它。

然后,在函数 calling color 中,您将根据 color 的结果调用 free

一般来说,您应该在free 一个指针值之后 调用malloc 来获取它(但当然要注意pointer aliases),但只能在那个指针(更准确地说,它指向的内存区域)是无用的。

编译时不要忘记启用所有警告和调试信息(如果使用GCC,则为gcc -Wall -g)。使用调试器 (gdb)。使用memory leak 检测器,例如valgrind

您应该阅读更多关于 C dynamic memory allocationvirtual address space 的信息。

如果在 Linux 上,另请阅读 Advanced Linux Programming。阅读Operating SystemsGarbage Collection 会有所帮助(至少对于相关概念而言)。

【讨论】:

  • You call malloc with a too small size 这是什么意思?
  • @GeorgeGkas 您的颜色代码每个可能有 5 个字节长,您需要两个大约 10 个字节的序列。您的输出每次都会比您分配的字节长。一旦您在 sprintf 调用中写入越界,行为就未定义。
  • @GeorgeGkas 您需要分配strlen(clr) + strlen(str) + strlen(CLR_RESET) + 1 字节来保存结果字符串。
【解决方案3】:

我的问题是,传递给之后的输出会发生什么 打印()?当我们退出函数 printf() 时,数组仍然是 分配?如果是,我们如何解除分配?有没有办法 写一个函数来为我们做这件事?

是的,color 函数结束后,你为output 分配的内存还在。需要存储color函数的返回值,使用完毕后调用free

是的,正如另一个答案中所述,在这种情况下,您传递给 malloc 的大小很小。


不过,您可以完全避免使用 malloc。 在调用者代码中声明一个字符串,例如

char someStr[100];

并传递给您的颜色函数

void color(char* output, const char* clr, char* str)
{
    /* Colorize string. */
    sprintf(output, "%s%s%s", clr, str, CLR_RESET);
}

在这里,不再需要 malloc。

电话:color(someStr, CLR_RED, "This is our own colorized string")

【讨论】:

  • 你知道在哪里存储它吗?在什么结构中?包含指针的链接列表?
  • @GeorgeGkas 不只是将其分配给 char *
  • 在后一种情况下,最好显式sizeof(someStr)传递给color并使用snprintf
  • @BasileStarynkevitch 是的,更安全,无论如何我认为调用者会通过创建足够大小的 someStr 来解决这个问题:) 只是向 OP 展示了这也是一种方法。
猜你喜欢
  • 1970-01-01
  • 2017-04-19
  • 2016-08-26
  • 1970-01-01
  • 1970-01-01
  • 2018-05-07
  • 1970-01-01
  • 2014-05-08
  • 2021-08-14
相关资源
最近更新 更多