23

是否可以在不调用的情况下从函数返回字符串malloc?我有一个功能如下:

char* getString(){
      char tempStr[20]

      // open file, read char by char and assign to tempStr
      // ....

      char* str = (char*)malloc(sizeof(char)*20);
      strcpy(str, tempStr); // assume this copy the null terminating char as well
      return str;
}

然后当我调用 时getString(),我将返回值分配给 a char*,然后在完成后释放它,如下所示:

void randomFunction(){
      char* line = NULL;
      int i = 0;
      while (i < 1000) {
           line = getString();
           // do stuff with line
           free (line);  
           line = NULL;
      }
}

但是,我想知道是否有任何方法可以做到这一点malloc?而且,这是从 C 函数返回字符串的正确方法吗?我试图做一些关于如何在没有的情况下返回的研究malloc,但没有找到明确的答案。我是 C 新手,仍在学习。

4

8 回答 8

14

您不能从函数中临时返回,除非您使用 malloc,否则函数中定义的字符数组将是临时的。另一种解决方案是将字符数组作为参数传递给函数并将其用作输出参数。

于 2013-03-16T20:02:56.273 回答
13

从函数返回字符串有三种常用方法。(嗯,实际上没有,但是有三种常见的方法可以返回指向字符串的指针,然后调用者可以使用它来访问字符串。)

  1. malloc()使用函数内部为字符串分配空间。这是最灵活的方法,但它使调用者负责free()分配的数组。它还可以强加一些性能开销。

  2. 要求调用者为字符串分配空间并传入指向该空间的指针。这给调用者带来了一些不便。特别是,调用者必须决定字符串的大小。

  3. 返回指向static函数内部定义的数组(的第一个元素)的指针。函数返回后数组会继续存在,但只有一个副本,这意味着后续调用将破坏之前调用返回的结果。这也意味着数组必须具有一些固定大小,在您编写代码时选择。

于 2013-03-16T20:07:00.167 回答
8

这取决于。

您可以决定并记录返回的字符串是指向某个静态内部缓冲区的指针。那么你的例程不是可重入的(也不是线程安全的)。例如ctimegetpwent这样做。

更好的做法是将结果字符串和大小作为参数传递,并填充该字符串并可能返回该字符串。getcwd(或snprintforstrftime返回大小,而不是指针)以这种方式工作。

但通常,您决定并记录返回的字符串是堆分配的,这是调用者free对它的责任。在这种情况下,您可能想要使用strdupasprintf

您可能会在整个程序中使用Boehm 的保守垃圾收集器(例如,将其GC_STRDUPorGC_MALLOC_ATOMIC用于字符串,以及GC_MALLOC用于包含一些指针的堆值。)

如果您觉得标准mallocstrdup太慢(但请先测量),您可以拥有自己的池分配器等。

您也可以有其他方案(但记录它们很重要)。例如,您可以返回一些interned string,甚至是规范的 interned string (有时称为“quark”或“symbol”) - 然后能够使用指针相等而不是字符串相等。您还可以有一些参考计数器方案。看看Glib(来自 GTK,但可在 GUI 程序之外使用!)提供的示例:GString-sGQuark-s字符串实用程序

然而,重要的是决定结果是否是堆分配的,并明确定义谁有责任释放(以及应该如何释放)堆分配的结果。

您可能想使用valgrind来追踪内存泄漏。不要忘记传递-Wall -g给您的gcc编译器!

PS。我会考虑使用 Boehm 的 GC。而且我认为malloc(或strdupasprintf....)不应该出于性能原因而被拒绝(您可以选择其他一些更快malloc的实现,或者使用您自己的内存池)。但是,内存泄漏可能是一个问题。

于 2013-03-16T20:03:25.960 回答
2

将内存分配移出函数的正常方法是传入指针。尽管在实践中,您希望确保您也不会越过缓冲区边界。

char* getString(char* tempStr){
          // open file, read char by char and assign to tempStr
          // ....

          return tempStr;
    }


void randomFunction(){
        char line[20];
        int i = 0;
        while (i < 1000) {
             getString(line);
             // do stuff with line

        }
  }
于 2013-03-16T20:06:03.537 回答
2

由于您的字符串(显然)总是 20 个字符,因此您可以简单地执行以下操作:

void getString( char *outputString ) {
    // do stuff to outputString instead of mallocing, or use local memory and copy it at the end
}

char line[20];
for( ... ) {
    getString( line );
    // do things with line
}

因为这避免了许多小型 malloc,所以速度更快。

于 2013-03-16T20:06:29.703 回答
2

在 C 中执行此操作的常用方法是将字符串作为参数传递给函数。

char *getString(char *str, int size){
      // open file, read char by char and assign to tempStr
      // ....

      strncpy(str, tempStr, size); // assume this copies the null terminator
      return str;
}

或者,您可以将字符串声明为静态,或者在文件或全局范围内,然后复制到其中。但是,请确保如果您将指向堆分配缓冲区的指针存储在此符号中,则在再次分配之前将其释放。

于 2013-03-16T20:08:24.747 回答
1

我不是 C 专家,但我认为这在 C 中是可能的。(这是基于 C++ 解决方案。)当然你需要知道字符串大小的界限(这里是 99),但你可以返回一个字符串无需分配,也无需输出参数。

https://onlinegdb.com/r1akUBiHB

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

typedef struct str_{
    char c[99];
} str;

str fun(int i){
    str ret;
    if(i > 5) strcpy(ret.c, "big");
    else      strcpy(ret.c, "small");
    return ret;
}

int main(int argc, char* argv[]){
    str ret = fun(argc);
    printf("%s", ret.c);
    return 0;
}

我不确定这是否取决于 C 强制执行称为返回值优化的东西。

另外我不知道您是否想要不分配,因为您根本不能或仅用于表演。如果是第二个,您可以实现一个结构,如果字符串不适合预定义的大小(此处为 99),则有条件地分配。

这基本上是std::stringC++ 中所做的,对于短字符串它不会在实践中分配。

请注意,如果这有效,它也将是线程安全的。(这里依赖全局变量的其他解决方案不是线程安全的。)

于 2019-09-03T01:48:10.770 回答
0

只需在函数中将字符串声明为静态并返回即可。

于 2013-03-17T03:11:05.703 回答