从函数返回 char* 和 char[] 有啥区别? [复制]

Posted

技术标签:

【中文标题】从函数返回 char* 和 char[] 有啥区别? [复制]【英文标题】:What is the difference between returning a char* and a char[] from a function? [duplicate]从函数返回 char* 和 char[] 有什么区别? [复制] 【发布时间】:2018-02-15 20:27:33 【问题描述】:

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

#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\n", foo1); // Prints "Hello, World"
    printf("------------\n");
    char* foo2 = function2(); // Prints nothing
    printf("%s\n", foo2);
    return 0;

【问题讨论】:

有许多可以使用的规范副本:What is the difference between char s and char *s?、String literals: Where do they go?、How to access a local variable from a different function using pointers?。 @Leushenko,不要这样想。这里的问题是关于函数的返回值,而不仅仅是char[]char * 之间的区别。 我厌倦了这里的管理员积极宣布写得很好的新手问题是重复的。 OP知道足够的C以对自己有帮助的方式提出问题。 “翻译”以另一种方式提出的问题通常需要已经具备 OP 正在努力培养的技能。 A duplicate is not the same as a bad question,与其他近距离投票不同,这并不意味着用户的问题无效。对我来说这个q。是罕见的“upvote+close”组合的一个例子:这是一个很好的第一个问题,但它需要属于更大的“数组变量与指针”问题系列才能完全理解,答案背后的基本事实是会是一样的。 @verisimilidude - 这个想法是将所有相似的问题相互联系起来,应该可以帮助人们在一个地方找到所有好的答案。如果不出意外,这有助于 Google 确定应该将哪个帖子放在顶部。 【参考方案1】:

第二个函数什么都不返回

第二个函数中的string数组:

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

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

string 在第一个函数中:

char* string = "Hello, World!";

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

【讨论】:

我认为这需要澄清一下。在这两种情况下,字符串"Hello, world!"; 都具有静态存储持续时间。在第一种情况下,我们创建一个名为string 的自动数组,并将静态字符串复制到自动数组中。在第二种情况下,我们创建了一个名为string 的自动指针,它指向静态存储区域。 没错。将行更改为static char string[] = "Hello, world!";,看看会发生什么。这会将数组的存储持续时间从自动更改为静态。 (另外,你应该养成声明不需要修改的数据const的习惯。) 这是否意味着所有char* 都是静态的?如果我写static char* string = "Hello, World!" 会改变什么吗? 这将使string 成为指向字符串文字的静态指针,在这种情况下只会浪费几个字节的内存。如果再次调用相同的函数,static 变量将仍然存在并已设置。在这种情况下char* stringchar string[] 之间的区别在于前者指向字符串常量,而后者在不同的内存块中对其进行临时副本。您通常会这样做,以便您可以修改副本。声明副本static 使数组在函数退出后保持不变。函数返回后依然有效。 这是一个字符串字面量,例如"Hello, world!",具有静态存储的情况。由于历史原因,字符串文字有点奇怪。将它们存储在 char* 而不是 const char* 中是合法的,以便与 const 存在之前编写的代码向后兼容。但是,当您尝试通过非const 指针修改字符串时,这会导致错误。将其声明为const char* const 会更好,因为它是指向不可修改内存的从未修改过的指针。【参考方案2】:

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

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

使用第二个函数 (function2) 情况略有不同。这里变量string 是函数内的局部变量。因此,一旦函数返回,它将超出范围并不再存在。使用此函数,您可以返回指向该数组第一个元素的指针,但该指针将立即变为无效,因为它将指向不再存在的东西。取消引用它(当你将它传递给printf 时会发生这种情况)将导致undefined behavior

【讨论】:

详细说明:function2 中还存在一个只读字符串,该字符串在函数退出后将继续存在。但是您没有返回指向它的指针。发生的情况是,您将该字符串中的数据复制到一个局部变量,然后返回一个指向 that 的指针。所以字符串仍然存在于某个地方,只是不在你的指针指向的位置。 @ComicSansMS 可能没有只读字符串。编译器倾向于将此类分配优化为移动少量数字而不是复制字符串。 @bezet 我想通过使用规范喜欢的“好像”术语可以更准确地表达答案,但我认为在这种情况下我们可以不用它。在编译器解释你的代码时,编译器的“头脑”中有这样一个只读字符串,该字符串是否被逐字发送到可执行文件中将由优化器确定。【参考方案3】:

在使用 C 或其他基于堆栈的语言进行编码时要记住的一件非常重要的事情是,当函数返回时,它(以及它的所有本地存储)都消失了。这意味着如果您希望其他人能够看到您的方法辛勤工作的结果,您必须将其放在您的方法停止后仍然存在的地方,这样做意味着您需要了解C在哪里存储东西以及如何存储。

您可能已经知道数组在 C 中是如何操作的。它只是一个按对象大小递增的内存地址,您可能还知道 C 不进行边界检查,因此如果您想访问第 11 个元素十个元素的数组,没有人会阻止你,只要你不尝试写任何东西,就没有坏处。你可能不知道的是,C 将这个想法扩展到了它使用函数和变量的方式。函数只是堆栈上按需加载的内存区域,其变量的存储只是该位置的偏移量。您的函数返回了一个指向局部变量的指针,特别是堆栈上保存“Hello World\n\0”的“H”的位置的地址,但是当您调用另一个函数(打印方法)时,内存是被 print 方法重用来做它需要的事情。你可以很容易地看到这一点(不要在生产代码中这样做!!!)

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

【讨论】:

是的,即使它可以处理静态字符串,这也是一个非常糟糕的习惯(恕我直言,无论如何)。【参考方案4】:

我认为这两个函数的返回值都是未定义的,因为它们返回的数据超出了范围。

没有。事实并非如此。

在函数function1 中,您正在返回指向字符串文字的指针。返回指向字符串文字的指针很好,因为字符串文字具有静态存储持续时间。但是自动局部变量就不是这样了。

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

return string; 

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

【讨论】:

@Jean-FrançoisFabre;其实我错了。字符串文字具有静态存储持续时间,因此从函数返回指向它的指针很好。 是的,我知道,但是今天有人学到了一些东西:) @Jean-FrançoisFabre;是的。这就是您从社区获得的回报,同时除了声誉外,还为社区做出贡献:) 这就是我喜欢这里的原因。要么学习,要么获得代表。有时两者都有:)【参考方案5】:

"Hello, World!" 是一个字符串文字,它有一个静态的存储持续时间,所以问题出在其他地方。您的第一个函数返回string,这很好。然而,第二个函数返回局部变量的地址string&amp;string[0] 相同),从而导致未定义的行为。您的第二个printf 语句不能打印任何内容,或者“Hello,World!”,或者完全是其他内容。在我的机器上,程序只是出现了分段错误。

始终查看编译器输出的消息。对于您的示例,gcc 给出:

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

这几乎是不言自明的。

【讨论】:

【参考方案6】:

我认为这两个函数的返回值都是未定义的,因为它们返回的数据超出了范围。

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

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

function2 中,从概念上讲,所指对象是本地数组string,它已被自动调整大小(在编译时),足以容纳字符串文字(当然包括空终止符),并且被用字符串的副本初始化。函数返回一个指向该数组的指针,除了该数组具有自动存储持续时间,因此在退出函数后不再存在(它确实是“超出范围”,用更熟悉的术语来说)。由于这是未定义的行为,编译器may in practice do all sorts of things.

这是否意味着所有char* 都是静态的?

同样,您需要区分指针和所指对象。指针指向数据;它们本身并不“包含”数据。

您已经到了应该正确研究 C 中实际上是什么数组和指针的地步——不幸的是,它有点乱。我可以临时提供的最佳参考是this,采用问答格式。

【讨论】:

以上是关于从函数返回 char* 和 char[] 有啥区别? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

matlab中的num2str函数和char函数在用法上有啥本质区别呀?

MSVC 中的 char 和 CHAR 有啥区别?

char a[] = ?string?; 有啥区别?和 char *p = ?string?;?

mysql中char和varchar有啥区别?

Char和VarChar有啥区别[重复]

char和varchar有啥区别