关于C#:从函数返回char*和char[]有什么区别?

What is the difference between returning a char* and a char[] from a function?

本问题已经有最佳答案,请猛点这里访问。

为什么第一个函数返回字符串"hello,world",而第二个函数什么也不返回。我认为这两个函数的返回值都是未定义的,因为它们返回的数据超出了范围。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
// This successfully returns"Hello, World"
char* function1()
{
    char* string ="Hello, World!";
    return string;
}
// This returns nothing
char* function2()
{
    char string[] ="Hello, World!";
    return string;
}

int main()
{
    char* foo1 = function1();
    printf("%s
"
, foo1); // Prints"Hello, World"
    printf("------------
"
);
    char* foo2 = function2(); // Prints nothing
    printf("%s
"
, foo2);
    return 0;
}


the second function returns nothing

第二个函数中的string数组:

1
char string[] ="Hello, World!";

具有自动存储期限。它在控制流从函数返回后不存在。

鉴于第一个函数中的string

1
char* string ="Hello, World!";

指向具有静态存储持续时间的文本字符串。这意味着,字符串在从函数返回后仍然存在。从函数返回的是指向这个文本字符串的指针。


关于字符串,您需要了解的第一件事是字符串字面实际上是一个只读字符数组,整个程序的生存期为。这意味着它们永远不会超出范围,它们将始终存在于程序的整个执行过程中。

第一个函数(function1的作用是返回指向此类数组第一个元素的指针。

对于第二个函数(function2),情况略有不同。这里的变量string是函数中的局部变量。因此,它将超出作用域,并在函数返回后停止存在。使用此函数,您将返回指向该数组第一个元素的指针,但该指针将立即变为无效,因为它将指向不再存在的某个元素。取消引用它(当您将它传递给printf时发生)将导致未定义的行为。


当使用C语言或其他基于堆栈的语言进行编码时,需要记住的一件非常重要的事情是,当函数返回时,它(及其所有本地存储)就消失了。这意味着,如果你想让别人能够看到你努力工作的方法的结果,你必须把它放在你的方法停止后仍然存在的地方,这样做意味着你需要了解C存储东西的位置和方式。

您可能已经知道数组是如何在C中运行的。它只是一个内存地址,该地址会随着对象的大小而递增,您可能还知道C不执行边界检查,因此如果您要访问10元素数组的第11个元素,没有人会阻止您,并且只要您不尝试编写任何内容,就不会造成任何伤害。您可能不知道的是,C将这个概念扩展到了它使用函数和变量的方式。函数只是堆栈上的一个内存区域,按需加载,其变量的存储只是与该位置的偏移量。您的函数返回了一个指向局部变量的指针,特别是保存"hello world "的"h"的堆栈上位置的地址,但当您调用另一个函数(print方法)时,print方法会重用该内存来执行所需的操作。您可以很容易地看到这一点(不要在生产代码中这样做!!!!)

1
2
3
4
5
6
char* foo2 = function2(); // Prints nothing
ch = foo2[0];  // Do not do this in live code!
printf("%s
"
, foo2);  // stack used by foo2 now used by print()
printf("ch is %c
"
, ch);  // will have the value 'H'!


I thought the return value of both of the functions would be undefined since they are returning data that is out of scope.

不,不是这样的。

在函数function1中,您将返回指向字符串文字的指针。返回字符串文本的指针很好,因为字符串文本具有静态存储持续时间。但对于自动局部变量来说,情况并非如此。

在函数function2中,数组string是一个自动局部变量,语句

1
return string;

返回指向自动局部变量的指针。一旦函数返回,变量string将不再存在。取消对返回指针的引用将导致未定义的行为。


"Hello, World!"是一个字符串文字,它有一个静态存储期限,所以问题在其他地方。第一个函数返回string的值,可以。但是,第二个函数返回局部变量的地址(string&string[0]相同),从而导致未定义的行为。你的第二份printf声明什么也写不出来,或者说"你好,世界!"或者别的什么。在我的机器上,程序只是出现了一个分段错误。

始终查看编译器输出的消息。例如,gcc给出了:

1
2
3
file.c:12:12: warning: function returns address of local variable [-Wreturn-local-addr]
    return string;
           ^

这是不言而喻的。


I thought the return value of both of the functions would be undefined since they are returning data that is out of scope.

两个函数都返回一个指针。重要的是指涉范围。

function1中,引用是字符串文字"Hello, World!",它具有静态存储持续时间。string是指向该字符串的局部变量,从概念上讲,返回指针的副本(实际上,编译器将避免不必要地复制值)。

function2中,从概念上讲,引用是本地数组string,它已自动调整大小(在编译时)以足以容纳字符串文字(当然包括空终止符),并已用字符串的副本初始化。函数将返回指向该数组的指针,但该数组具有自动存储持续时间,因此在退出函数后不再存在(在更熟悉的术语中,它确实"超出范围")。由于这是未定义的行为,编译器实际上可以做各种事情。

Does that mean that all char* are static?

同样,您需要区分指针和引用。指针指向数据;它们本身并不"包含"数据。

你已经到了一个应该正确研究数组和指针在C中的位置了——不幸的是,这有点混乱。我现在能提供的最好的参考资料就是这个,以Q&A格式。