从C函数返回字符串

时间:2014-09-12 00:33:45

标签: c string return local-variables

我在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函数),但我无法正确显示它(我得到了垃圾)。

帮助表示赞赏。

8 个答案:

答案 0 :(得分:50)

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

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

答案 1 :(得分:8)

char word[length];
char *rtnPtr = word;
...
return rtnPtr;

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

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

答案 2 :(得分:3)

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

为了完成您尝试做的事情,您需要执行以下操作之一:

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

答案 3 :(得分:2)

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

相反 char *rtnPtr = word;

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

因此它在main函数中可用。使用后释放内存。

答案 4 :(得分:1)

word位于堆栈中,只要getStr()返回就会超出范围。您正在调用未定义的行为。

答案 5 :(得分:1)

正如其他人已经说过的,如果不将非常量字符串分配到堆上(例如使用 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 会将 struct 中的前几个字符解释为字符指针,这肯定是无效的。

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

===杰克

答案 6 :(得分:0)

更简单:返回一个指向字符串的指针,该字符串与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;
}

答案 7 :(得分:0)

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

对于那些不熟悉它的人,Cython允许你静态输入你需要加速的Python代码。所以这个过程是,喜欢编写Python :),在某个地方找到它有点慢,对它进行分析,对一个或两个函数进行生成并对它们进行cython化。哇。接近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
)