【问题标题】:Returning strings in C function, explanation needed在 C 函数中返回字符串,需要解释
【发布时间】:2010-08-18 01:43:56
【问题描述】:

我知道为了从函数返回一个字符串,我必须返回一个指针。我不明白为什么 char 数组的处理方式与整数有所不同,并且在退出函数时会被销毁。这可能是因为我来自高级语言世界,但这对我来说似乎同样有效:

int x = 1;
return x;

char x[] = "hello";
return x;

【问题讨论】:

  • 你应该展示你心目中的用例---我能猜到,但这是一个猜测,具体的答案取决于你想做什么......
  • 这里有一个很好的例子,说明如何在 C 语言中使用字符串和指针做不同的事情 stackoverflow.com/a/46344713/5842403

标签: c pointers char


【解决方案1】:

原因既简单又微妙:C 函数不能声明为返回数组。

当你返回一个指针时,像这样:

char *foo(void)
{
    char x[] = "hello";
    return x;
}

return x;实际上返回了x。它返回一个指向x1 的第一个元素的指针 - 这与说的完全相同:

return &x[0];

应该更清楚为什么这是不正确的 - 它与此完全相似:

int *foo(void)
{
    int x = 100;
    return &x;
}

但是,可以从函数返回 结构 - 因此,只要数组包含在 struct 中,您就可以返回数组。下面的就OK了:

struct xyzzy {
    char x[10];
};

struct xyzzy foo(void)
{
    struct xyzzy x = { "hello" };
    return x;
}


1。这是数组类型的特殊情况规则的结果。在表达式中,如果数组不是一元 &sizeof 运算符的主题,则它的计算结果为指向其第一个元素的指针。试图在 C 中确定一个实际的数组有点像试图抓住你手中的雾——它只是消失了,取而代之的是一个指针。

【讨论】:

  • 我明白你的回答,但我有点困惑。在代码块 #1 中,C 自动返回一个指针(例如,将“return x;”转换为“return &x[0];”,因为我们将函数声明为返回指针的函数(使用 *),或者代码只是无效?
  • @snitko: C 把它和return &x[0]; 一样对待不是因为返回类型,而是因为(除了两个特定的例外,在我的脚注中提到),x 总是 在表达式中被视为&x[0]
  • 没有“自动返回”,尝试返回一个布尔值,它会爆炸。它起作用的原因是char x[] 是一个“数组”,因此它是一个指针(!)。您也可以使用与char x[] 完全相同的char *x = "hello"[] 只是语法糖。 C a 中的数组仅指向内存中“数组”的起始位置。
  • @Ivo Wetzel:不,数组与指针相同。这是一种常见但不正确的理解。特别是,char *x = "hello";x 声明为指针,而 char x[] = "hello";x 声明为 6 个字符的数组(尝试在这两种情况下打印 sizeof x!)。
  • @Ivo Wetzel:不,数组不是指针,正如 caf 的脚注中所解释的那样。此外,char *x = "hello" 一点也不像char x[] = "hello",第一种方法实际上可以正常工作,因为它会返回一个指向字符串文字的指针。
【解决方案2】:

整数是原始类型,所以它的值是这样返回的。但是,在 C 中,字符串不是原始类型,这就是为什么您将其声明为字符数组的原因。当你返回一个数组时,你实际上是在返回一个指向原始数组的指针,所以如果你愿意的话,它是一种引用返回。但是如您所见,一旦您退出函数,指向char x[] 的指针就无效了。

【讨论】:

  • 所以只有 C 中的原始类型可以从函数返回而不使用指针,对吗?
  • 不,作为 caf 点,您还可以返回一个结构。关键是您正在返回指向其他东西(您的数组)的指针,并且其他东西恰好位于易失性空间(函数的堆栈框架)中。一旦它消失,指针就不再有效。
  • @snitko:不,聚合类型也可以从函数返回。只有数组类型不能直接返回。
【解决方案3】:

请参阅我写的关于this 的详细答案。不仅如此,在 C 中,在函数范围内创建的变量正在使用自动堆栈堆,变量将驻留在其中,当函数返回时,该堆被销毁,这是使用局部变量特别是类型的地方string 即 char [] 或指向 string 的指针必须在函数范围之外的堆上分配,否则将返回垃圾。解决方法是使用静态缓冲区或使用malloc

【讨论】:

  • 我发现这个答案对自动(堆栈)变量具有误导性。 “编译器和链接器为符号名称提供了 0x1234 的内存地址。”似乎暗示自动数组具有编译时固定地址。显然这是不正确的;它们被分配在堆栈上。如果它在编译时被修复,递归将是不可能的。静态数组确实有固定地址。
  • @Matthew:不要学究气,这只是一个例子,它是我向一个完整的初学者解释的方式......数组和指针的细微差别...... :)
【解决方案4】:

该 char 数组是在函数范围内分配的(而不是通过 malloc() 等动态分配),因此它在该范围之外将不可用。

两种方法:

  1. 将该字符数组声明为static
  2. 在调用函数中分配缓冲区,将其作为参数传递,然后此函数将填充它。

【讨论】:

  • 如果字符数组是静态的,你最好希望它只在这个函数内部使用(也就是说,函数永远不会将静态字符数组的引用返回给函数的调用者),除非你想要大量有趣的多线程问题和“嘿,为什么我的字符串只是无缘无故地改变了?”类型问题。即使它只是在函数内部使用,将其作为静态仍然会导致多线程问题。
  • 是的,这都是真的。如果您多次运行该函数,请不要这样做,尤其是在多线程时。 PS 我的想法来自vijayinterviewquestions.blogspot.com/2007/07/…
猜你喜欢
  • 1970-01-01
  • 2015-11-06
  • 1970-01-01
  • 1970-01-01
  • 2011-11-23
  • 2017-02-16
  • 2022-11-10
  • 2014-11-06
相关资源
最近更新 更多