从C函数返回字符串

MrW*_*olf 30 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;
}
Run Code Online (Sandbox Code Playgroud)

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

感谢帮助.

mic*_*yer 57

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

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

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

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

char *getStr(void) {
    static char wordd[10 + 1];
    ...
    return wordd;
}
Run Code Online (Sandbox Code Playgroud)

或者在堆上分配字符串:

char *getStr(int length) {
    char *wordd = malloc(length + 1);
    ...
    return wordd;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您执行malloc,则调用方应在指针上调用free以释放内存 (2认同)
  • 这是一个愚蠢的问题,但是您需要返回调用者传入的字符串吗?我是直接修改的,所以我不明白为什么我必须返回它,对吗? (2认同)
  • 你应该指出第二种方法的缺点,即静态缓冲区将在下一次调用时被覆盖,因此该方法不是线程安全的,并且如果用户最终调用,即使在单线程代码中也会产生意外的结果该方法仍然具有对第一个字符串的引用. (2认同)
  • ...我认为您还应该指出,第三种方法要求调用者通过 `free` 作为 API 的一部分来释放字符串。 (2认同)
  • @michaelmeyer 在堆上分配时如何释放内存? (2认同)

nne*_*neo 8

char word[length];
char *rtnPtr = word;
...
return rtnPtr;
Run Code Online (Sandbox Code Playgroud)

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

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


Bri*_*ell 7

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

为了执行您想要执行的操作,您需要执行以下操作之一:

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


Jac*_*mit 6

正如其他人已经说过的,如果不在堆上分配非常量字符串(例如使用 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;
}
Run Code Online (Sandbox Code Playgroud)

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

有(可以说)一些缺点:

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

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

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

===江淮汽车


Arp*_*pit 5

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

反而 char *rtnPtr = word;

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

这样就可以在主函数中使用了。使用后释放内存。