【问题标题】:returning string from function without malloc从没有malloc的函数返回字符串
【发布时间】:2013-03-05 10:42:19
【问题描述】:

是否可以在不调用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() 时,我将返回值分配给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 新手,还在学习。

【问题讨论】:

  • 你的 malloc 调用最好写成 char *str = malloc(20); sizeof (char) 定义为 1,并且强制转换 malloc 的结果是不必要的,not recommended
  • @KeithThompson 我从来不知道强制转换 malloc 的结果会带来不良行为。谢谢(你的)信息! :)
  • @KeithThompson 我不建议省略 sizeof(char);将它保留在那里是一个好习惯,这将是一个糟糕的编译器,不会对其进行优化。此外,反对强制使用 malloc 的论点对我来说总是很愚蠢。它在 C++ 中是必需的,在我看来,您最好尽可能编写语言可移植的代码。
  • @Dave:至于转换malloc 的结果,在这种情况下,转换没有真正的目的。一般来说,演员阵容应该被怀疑。我不同意编写语言可移植代码的优点。从 C++ 调用 C 代码很容易,反之亦然;编写可以编译为 C 或 C++ 的代码几乎没有任何真正的优势。决定你用什么语言写作,然后用那种语言写作。
  • @Dave 在 C++ 中你使用new,所以关于malloc 的争论是没有意义的。

标签: c pointers malloc


【解决方案1】:

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

【讨论】:

  • 我会尝试您的建议,将 char 数组作为参数传递。如果我正确地获得了您的解决方案,该函数将修改并返回作为输入传递的相同字符数组,对吗?这样我们就可以避免malloc,对吗?谢谢
  • @blenzcoffee 如果我理解正确你想做什么,那么应该这样做。
  • 如果数组是由调用者以一定大小声明的,则可以避免使用malloc。
【解决方案2】:

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

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

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

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

【讨论】:

    【解决方案3】:

    视情况而定。

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

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

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

    您可以在整个程序中使用Boehm's conservative garbage collector(例如,将其GC_STRDUPGC_MALLOC_ATOMIC 用于字符串,将GC_MALLOC 用于包含一些指针的堆值。)

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

    您也可以有替代方案(但记录它们很重要)。例如,您可以返回一些interned string,甚至是规范的内嵌字符串(有时称为“quark”或“symbol”)——然后能够使用指针相等而不是字符串相等。你也可以有一些reference counter 方案。看看Glib(来自 GTK,但可在 GUI 程序之外使用!)提供的示例:GString-sGQuark-sstring utilities

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

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

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

    【讨论】:

      【解决方案4】:

      由于您的字符串(显然)总是 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,所以速度更快。

      【讨论】:

        【解决方案5】:

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

        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
        
                }
          }
        

        【讨论】:

        • 在 Linux 上,我建议strdupasprintf
        • strdupasprintf 使用 malloc,这是 OP 专门试图避免的。
        • 您的代码将line 设置为我认为是getString 的局部变量。我想你是想返回tempStr,我不知道你为什么要strcpy,或者完全重新分配line
        • @Dave 只是修改了问题代码,删除了 line = 和 str :)
        【解决方案6】:

        在 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;
        }
        

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

        【讨论】:

          【解决方案7】:

          我不是 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::string 在 C++ 中所做的,对于短字符串,它实际上不会分配。

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

          【讨论】:

            【解决方案8】:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2010-12-14
              • 2014-12-07
              • 2014-11-06
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多