【问题标题】:C function returning a stringC函数返回一个字符串
【发布时间】:2020-04-02 14:19:59
【问题描述】:

我尝试在 C 中创建一种 toString 函数,如下所示:

struct esploratore {
    char nome[20];
    char cognome[20];
    int nascita;
    int morte;
};

char *esploratoreToString(esploratore e) {
    char *s = (char*) malloc(50 * sizeof(char));
    snprintf(s, 50,
             "%s %s %d %d",
             e.nome, e.cognome, e.nascita, e.morte);
    return s;
}

并以这种方式在main 中使用它:

printf("%s", esploratoreToString(e));

它可以工作,但问题是:malloc 分配的内存会被释放吗?

这是唯一的解决方案吗?

char *s = esploratoreToString(e);
...
printf("%s\n\n", s);
free(s);

【问题讨论】:

  • 不,它不会被释放,除非在你完成 steing 后在调用函数中释放它

标签: c malloc free


【解决方案1】:

在您的原始代码中,内存不会被释放。这是内存泄漏。

要正确调用此函数,您必须这样做

char *s = esploratoreToString(e);
printf("%s", s);
free(s);

如果您要让esploratoreToString 进行分配,实际上没有任何办法解决这个问题。 C 中动态分配的对象永远不会释放自己。 (C++ 是另一回事……)

(当然esploratoreToString 应该在继续之前检查malloc 是否成功...)

【讨论】:

  • 我对它进行了修改并修改了问题。 TY
  • 好吧,如果malloc() 失败,snprintf() 写入生成的NULL 指针的第一个字节将在几乎所有抢占式多任务系统上安全地使进程崩溃。是的,是UB,是的,迂腐的方法是检查它,但大多数软件不需要担心。
  • 比起asprintf(),我更担心snprintf()的使用,而不是NULL的缺失检查:几乎任何snprintf()的使用都有可能产生截断的结果,可能会或可能不会让坏事发生。使用asprintf() 来代替此类错误是完全没有必要的,并且很容易避免。它实际上会通过完全避免 malloc() 调用来简化代码。
【解决方案2】:

是的。这是 C 中的一个常见习惯用法:被调用者分配内存并将所有权转移给调用者。这意味着调用者现在负责释放分配的块。

【讨论】:

    【解决方案3】:

    malloc()分配的内存在调用free()时会被释放,否则进程退出时会被释放。作为现在的设计,调用代码负责在完成后释放内存。或者,您可以重新设计 exploratoreToString() 以提供缓冲区并将其大小作为参数。

    【讨论】:

      【解决方案4】:

      还有一个替代方案,您可以在其中提供缓冲区:

      char *esploratoreToString(esploratore *e, char *buf, int bufsz) {
          snprintf(buf, bufsz,
                   "%s %s %d %d",
                   e.nome, e.cognome, e.nascita, e.morte);
          return buf;
      }
      
      ...
      
      int main()
      {
          char buf[100];
          esploratore exp = {...};
      
          printf("%s\n", esploratoreToString(&exp, buf, sizeof buf);
      }
      

      因此您可以动态分配它,或者作为自动堆栈分配的缓冲区。

      另一种选择是返回一个指向静态分配缓冲区的指针,如:

      char *esploratoreToString(esploratore *e) {
          static char buf[50];
          snprintf(buf, bufsz,
                   "%s %s %d %d",
                   e.nome, e.cognome, e.nascita, e.morte);
          return buf;
      }
      

      但这有一个问题是它不可重入,而且如果你需要在同一次调用printf(3)main)中调用它两次,第二次调用将覆盖缓冲区,你会得到一个奇怪的由于用下一个结果覆盖一个结果并打印两次相同的结果而导致的行为。

      【讨论】:

      • 此版本无法检测字符何时不适合缓冲区,对于 OP 的用例可能重要也可能不重要
      • 当然不是……但我认为这不是问题,因为问题和我一样。
      • 从外部你有更好的环境上下文来决定你传递什么样的缓冲区。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-30
      • 2013-12-08
      • 1970-01-01
      • 2013-04-14
      • 2014-11-06
      相关资源
      最近更新 更多