为什么第一个函数返回字符串"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;
} |
- @哈克斯怎么不是复制品?一个版本工作而另一个版本不工作的原因是,在一种情况下,函数返回指向局部变量的指针。
- 有许多规范的重复可以使用:char s和char*s有什么区别?,字符串文本:它们去哪里?,如何使用指针从其他函数访问局部变量?.
- @卢申科,不要想。这里的问题是函数的返回值,而不仅仅是char[]和char *之间的差异。
- 我厌倦了这里的管理员们积极地宣称写得好的新手问题是重复的。行动小组知道的足够多,可以用对自己有帮助的方式问这个问题。翻译"以另一种方式提出的问题往往需要已经具备行动计划所要培养的技能。
- 一个重复的问题和一个糟糕的问题不一样,而且不像其他近距离投票,这并不意味着用户的问题是无效的。对我来说,这个问题是一个罕见的"上投票+下投票"组合的例子:这是一个很好的第一个问题,但它需要属于更大的"数组变量对指针"问题系列,以便充分理解,答案背后的基本事实将是相同的。
- 另外,如果需要打印未格式化的输出,最好使用fputs()函数。另外,如果您希望使用新的行,则可以选择puts()函数。
- @真实性——这种想法是把所有相似的问题联系起来,帮助人们在一个地方找到所有的好答案。如果没有别的,那就可以帮助谷歌找到他们应该放在最上面的帖子。
- @任何有足够经验的人都可以把问题联系起来。管理员/主持人很少干预
- @真实性:正如梁申科所说,复制并不意味着问题的质量不好。但它将同一类别的问题链接在一起,使它们出现在页面右侧的"链接"块中。这样,询问者以及后来的seacher可以查看更多的资源和不同的方法。
the second function returns nothing
第二个函数中的string数组:
1
| char string[] ="Hello, World!"; |
具有自动存储期限。它在控制流从函数返回后不存在。
鉴于第一个函数中的string:
1
| char* string ="Hello, World!"; |
指向具有静态存储持续时间的文本字符串。这意味着,字符串在从函数返回后仍然存在。从函数返回的是指向这个文本字符串的指针。
- 我认为这需要一些澄清。在这两种情况下,字符串"Hello, world!";都具有静态存储持续时间。在第一种情况下,我们创建一个名为string的自动数组,并将静态字符串复制到自动数组中。在第二种情况下,我们创建一个名为string的自动指针,该指针指向静态存储区域。
- 确切地。把线改成static char string[] ="Hello, world!";,看看会发生什么。这会将阵列的存储时间从自动更改为静态。(另外,您应该养成声明不需要修改const的数据的习惯。)
- 这是否意味着所有的char*都是静态的?如果我写了static char* string ="Hello, World!",那会有什么改变吗?
- 这将使string成为指向字符串文字的静态指针,在本例中,这只会浪费几个字节的内存。如果您再次调用同一个函数,那么static变量仍然存在并已设置。在这种情况下,char* string和char string[]的区别在于前者指向字符串常量,后者在不同的内存块中对其进行重新复制。您通常会这样做,以便修改副本。声明副本static使数组在函数退出后保持不变。它在函数返回后仍然有效。
- 在这种情况下,字符串文字(如"Hello, world!")具有静态存储。由于历史原因,字符串文字有点奇怪。将它们存储在char*而不是const char*中是合法的,因为它与const之前编写的代码向后兼容。但当您尝试通过非const指针修改字符串时,这会导致错误。声明为const char* const会更好,因为它是指向不可修改内存的从未修改过的指针。
- @眠__I think it will be very world adding the information in the top rated comment to your others excellent answer.
关于字符串,您需要了解的第一件事是字符串字面实际上是一个只读字符数组,整个程序的生存期为。这意味着它们永远不会超出范围,它们将始终存在于程序的整个执行过程中。
第一个函数(function1的作用是返回指向此类数组第一个元素的指针。
对于第二个函数(function2),情况略有不同。这里的变量string是函数中的局部变量。因此,它将超出作用域,并在函数返回后停止存在。使用此函数,您将返回指向该数组第一个元素的指针,但该指针将立即变为无效,因为它将指向不再存在的某个元素。取消引用它(当您将它传递给printf时发生)将导致未定义的行为。
- 详细说明:在function2中,还存在一个只读字符串,该字符串在函数退出后将继续存在。但你没有返回指向它的指针。发生的情况是,您将数据从该字符串复制到一个局部变量,然后返回一个指向该变量的指针。所以字符串仍然存在于某个地方,只是不在指针指向的位置。
- @可能没有只读字符串。编译器倾向于将这种分配优化为移动几个数字,而不是复制字符串。
- @Bezet我想,通过使用规范喜欢的"好像"术语,答案可能会更准确地表达出来,但我认为在这种情况下,我们可以不使用它就摆脱困境。编译器的"头脑"中有这样一个只读字符串,它解释代码时,该字符串是否会逐字发送到可执行文件中,将由优化器决定。
当使用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'! |
- 是的,即使Sorta与静态字符串一起工作,它也是一个很坏的习惯(不管怎样,imho)。
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是一个自动局部变量,语句
返回指向自动局部变量的指针。一旦函数返回,变量string将不再存在。取消对返回指针的引用将导致未定义的行为。
- @Jean-Fran和Oisfabre;实际上我错了。字符串文本具有静态存储持续时间,因此从函数返回指向它的指针是可以的。
- 是的,我知道,但今天有人学到了一些东西:)
- @Jean Fran&231;Oisfabre;是的。这是你从社区中得到的奖励,除了声誉之外,你还为社区做出了贡献。
- 所以我喜欢这里。要么你学习要么你得到代表。有时两者都是:)
"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格式。