【问题标题】:Returning string from C function从 C 函数返回字符串
【发布时间】:2014-11-06 01:59:51
【问题描述】:

我已经 3 年多没用过 C 了,我对很多东西都生疏了。

我知道这可能看起来很愚蠢,但我目前无法从函数返回字符串。请假设:我不能为此使用string.h

这是我的代码:

#include <ncurses.h>

char * getStr(int length)
{   
    char word[length];

    for (int i = 0; i < length; i++)
    {
        word[i] = getch();
    }

    word[i] = '\0';
    return word;
}

int main()
{
    char wordd[10];
    initscr();
    *wordd = getStr(10);
    printw("The string is:\n");
    printw("%s\n",*wordd);
    getch();
    endwin();
    return 0;
}

我可以捕获字符串(使用我的getStr 函数),但我无法正确显示它(我得到垃圾)。

感谢您的帮助。

【问题讨论】:

  • 您可以使用ncurses.h,但不能使用string.h?好奇怪的环境……
  • 你创建一个可变长度数组char word[length];安全问题),然后将其地址分配给rtnPtr——函数结束销毁本地char数组word[length] .您正在返回一个指向不再存在的东西的指针。
  • @AshishAhujaツ:这是 2014 年课程中的作业要求之一。它本来是具有挑战性的,教授认为简单地使用 string.h 太容易了。我希望这能为问同样问题的人澄清一下。干杯。
  • @AshishAhujaツ:这些信息无关紧要。要求就在那里,这绝对是最重要的。 ;)

标签: c string return local-variables


【解决方案1】:

在调用方的堆栈上分配字符串并将其传递给您的函数:

void getStr(char *wordd, int length) {
    ...
}

int main(void) {
    char wordd[10 + 1];
    getStr(wordd, sizeof(wordd) - 1);
    ...
}

或者将getStr中的字符串设为静态:

char *getStr(void) {
    static char wordd[10 + 1];
    ...
    return wordd;
}

或者在堆上分配字符串:

char *getStr(int length) {
    char *wordd = malloc(length + 1);
    ...
    return wordd;
}

【讨论】:

  • 如果你做 malloc 调用者应该在指针上调用 free 以释放内存
  • 这是一个愚蠢的问题,但是你需要返回调用者传入的字符串吗?我是直接修改的,所以我不明白为什么要退货,对吧?
  • 您应该指出第二种方法的缺点,即静态缓冲区将在下次调用时被覆盖,因此该方法不是线程安全的,即使在单次调用中也会产生意想不到的结果如果用户在仍然引用第一个字符串的情况下再次调用该方法,则线程代码。
  • ...我认为您还应该指出,第三种方法要求调用者通过free 释放字符串作为API 的一部分。
  • @michaelmeyer 在堆上分配时如何释放内存?
【解决方案2】:
char word[length];
char *rtnPtr = word;
...
return rtnPtr;

这不好。您正在返回一个指向自动(作用域)变量的指针,该变量将在函数返回时被销毁。指针将指向一个被破坏的变量,这几乎肯定会产生“奇怪”的结果(未定义的行为)。

您应该使用malloc(例如char *rtnPtr = malloc(length))分配字符串,然后稍后在main 中分配free

【讨论】:

    【解决方案3】:

    你在堆栈上分配你的字符串,然后返回一个指向它的指针。当您的函数返回时,任何堆栈分配都将变为无效;指针现在指向堆栈上的一个区域,该区域可能会在下次调用函数时被覆盖。

    为了做你想做的事,你需要做以下事情之一:

    1. 使用malloc 或类似方法在堆上分配内存,然后返回该指针。调用者需要在内存完成后调用free
    2. 在调用函数(将使用字符串的函数)的堆栈上分配字符串,并将指针传递给函数以将字符串放入。在调用函数的整个调用过程中,其栈上的数据都是有效的;只有当你返回堆栈分配的空间时,它才会被其他东西使用。

    【讨论】:

      【解决方案4】:

      您的指针指向函数的局部变量。因此,一旦您从函数返回,内存就会被释放。您必须在堆上分配内存才能在其他功能中使用它。

      相反 char *rtnPtr = word;

      这样做 char *rtnPtr = malloc(length);

      以便在主函数中可用。使用后释放内存。

      【讨论】:

        【解决方案5】:

        正如其他人已经说过的,如果不将非常量字符串分配到堆上(例如使用 strdup),就无法以有用的方式返回非常量字符串。但是在所有最新版本的 C 标准(如果我没记错的话,C89 和更高版本)中,您可以返回一个结构。调用者不需要释放结果,因为它不在堆上。而且它是线程安全的。

        #include <stdio.h>
        
        struct stringbuf
        {
          char buf[40];
        };
        
        struct stringbuf getanswer(int i)
        {
          struct stringbuf result = { 0 };
        
          snprintf(result.buf, sizeof(result.buf), "The answer is %d", i);
        
          return result;
        }
        
        int main(int argc, char **argv)
        {
          /*
           * Remember to pass the .buf member, not the struct, to functions
           * such as printf which expect a character pointer as argument!
           * Passing the result of getanswer in the next line without .buf
           * appended, will likely crash the program because the program
           * will put the entire struct on the stack, not a character
           * pointer, and will make printf interpret the first few bytes
           * of the string as a pointer. That would be bad.
           */
          printf("How many arguments did I get? %s\n", getanswer(argc).buf);
          return 0;
        }
        

        注意:为了使示例代码尽可能简单和集中,我只是声明了一个没有 typedef 的结构类型。通过使用 typedef 并返回定义的类型,您可以节省大量的输入。

        有(可以说)一些缺点:

        • 返回结构的函数不能返回 NULL。
        • 结构中缓冲区的大小是固定的,因为编译器必须在编译时知道返回类型的大小。
        • 返回结构的函数的结果可能存储在堆栈中;这可能是没有大量堆栈空间的小型系统(如微控制器)的问题。
        • 与字符数组不同,结构的实例不是存储在其中的字符串的可用别名。换句话说,虽然您可以创建一个字符数组并将其名称用作指向第一个字符的指针,但您不能将结构的名称用作指针。

        最后一点很重要,因为您必须记住,具有字符数组的结构与数组本身不同。所以如果你想调用一个字符串函数,你应该传递字符串成员变量,而不是指向结构的指针。这对于具有可变参数的函数(例如 printf 和朋友)尤其重要,如果您做错了编译器可能不会警告您:传递结构会将整个结构放在堆栈上,而不仅仅是指向第一个字符的指针。 printf 会将结构体中的前几个字符解释为字符指针,这肯定是无效的。

        是的,可以将指向结构的指针转换为 char * 并将其传递给字符串函数(包括 printf),这样可以正常工作,但我认为这样做是不好的做法:如果您 (或其他人)曾经决定将另一个成员变量放在字符串缓冲区前面的结构声明中,任何使用指向结构的类型转换指针假设字符串缓冲区从结构开始的地方开始,静默 em>失败。你可能想避免这种情况,所以即使有点不方便,也要使用指向字符串成员变量的指针。

        ===贾克

        【讨论】:

          【解决方案6】:

          word 在堆栈上,一旦getStr() 返回,就会超出范围。您正在调用未定义的行为。

          【讨论】:

            【解决方案7】:

            我在了解 Cython 时遇到了这个帖子。我对原始问题的扩展可能对在 C/Cython 界面工作的其他人有用。所以这是原始问题的扩展:如何从 C 函数返回一个字符串,使其可用于 Cython,从而可用于 Python?

            对于那些不熟悉它的人,Cython 允许您静态键入需要加速的 Python 代码。所以这个过程是,享受编写 Python :),发现它在某个地方有点慢,分析它,分解一两个函数,然后对它们进行 cythonize。哇。接近 C 的速度(它编译为 C) 已修复。耶。另一个用途是将 C 函数或库导入 Python,就像这里所做的那样。

            这将打印一个字符串并将相同或另一个字符串返回给 Python。有3个文件,c文件c_hello.c,cython文件sayhello.pyx,cython设置文件sayhello.pyx。当它们使用python setup.py build_ext --inplace 编译时,它们会生成一个共享库文件,该文件可以导入 python 或 ipython 并运行 sayhello.hello 函数。

            c_hello.c

            #include <stdio.h>
            
            char *c_hello() {
              char *mystr = "Hello World!\n";
              return mystr;
              // return "this string";  // alterative
            }
            

            sayhello.pyx

            cdef extern from "c_hello.c":
                cdef char* c_hello()
            
            def hello():
                return c_hello()
            

            setup.py

            from setuptools import setup
            from setuptools.extension import Extension
            from Cython.Distutils import build_ext
            from Cython.Build import cythonize
            
            
            ext_modules = cythonize([Extension("sayhello", ["sayhello.pyx"])])
            
            
            setup(
            name = 'Hello world app',
            cmdclass = {'build_ext': build_ext},
            ext_modules = ext_modules
            )
            

            【讨论】:

              【解决方案8】:

              更简单:返回一个指针,该指针指向一个用 strdup 分配的字符串。

              #include <ncurses.h>
              
              char * getStr(int length)
              {   
                  char word[length];
              
                  for (int i = 0; i < length; i++)
                  {
                      word[i] = getch();
                  }
              
                  word[i] = '\0';
                  return strdup(&word[0]);
              }
              
              int main()
              {
                  char wordd[10];
                  initscr();
                  *wordd = getStr(10);
                  printw("The string is:\n");
                  printw("%s\n",*wordd);
                  getch();
                  endwin();
                  return 0;
              }
              

              【讨论】:

              • 正常情况下,这会很好用,但是这个用户不能使用string.h,而strdup是那个头文件的一部分。
              • strdup( ) 在堆上分配内存来复制字符串。一个表现良好的程序应该检查 strdup 不返回 NULL,并且应该在不再需要结果时释放()结果。
              猜你喜欢
              • 2011-05-29
              • 2013-03-25
              • 1970-01-01
              • 2021-05-12
              • 2023-03-17
              • 2011-04-14
              相关资源
              最近更新 更多