我是C的新手。我已经从上一个问题开始扩展了这个问题:Strange behavior when returning "string" with C(顺便感谢所有回答或评论该问题的人。)
很简单:
为什么可以工作:
#include <stdio.h>
int func() {
int i = 5;
return i;
}
int main() {
printf("%d",func());
}
但不是这样:
#include <stdio.h>
char * func() {
char c[] = "Hey there!";
return c;
}
int main() {
printf("%s",func());
}
从上一个问题来看,
int i
在逻辑上也不应该存在,因为该函数已经return
,但是为什么在char c[]
不能返回的情况下仍然可以返回它呢?
(它似乎与“ Pointers and memory scope”重复,但我想了解更多有关返回int
和char *
之间的区别的信息。)
答案 0 :(得分:7)
问题没有返回char *
,而是返回了在堆栈上分配的内容。
如果为字符串分配内存而不是指向函数堆栈,则不会有问题。像这样:
char * func() {
char c[] = "Hey there!";
return strdup(c);
}
int main() {
char* str = func();
printf("%s", str);
free(str);
}
重要的是要提到,在两种情况下,您都在复制一个值,并且两种情况下复制的值都是正确的,但是复制的值的含义不同。
在第一种情况下,您正在复制一个int
值,并且从函数返回后,您将使用该int
value 有效。但是在第二种情况下,即使您有一个有效的指针值,它也会引用一个无效的内存地址,这是被调用函数的堆栈。
根据评论中的建议,我决定在此代码的内存分配中添加另一种更好的做法:
#define NULL (void*)0
int func(char *buf, int len) {
char c[] = "Hey there!";
int size = strlen(c) + 1;
if (len >= size) {
strcpy(buf, c);
}
return size;
}
int main() {
int size = func(NULL, 0);
char *buf = calloc(size, sizeof(*buf));
func(buf, size);
printf("%s", buf);
free(buf);
return 0;
}
许多Windows API函数都使用类似的方法。这种方法更好,因为指针的所有者更明显(此处的main
)。
答案 1 :(得分:3)
在第一个示例中,返回值被复制。在第二个示例中,您将返回一个指针,该指针将指向不再存在的内存位置。
答案 2 :(得分:3)
在第一种情况下,您从函数返回int
值5。然后可以打印该值。
但是,在第二种情况下,您返回的类型为char *
。该值指向函数func
局部的数组。该函数返回后,数组超出范围,因此指针指向无效的内存。
这两种情况之间的区别是您直接使用的值,而不是不再指向有效内存的指针值。如果您返回了一个指向malloc
分配的内存的指针,那么该指针将指向有效的内存。
答案 3 :(得分:3)
您正在尝试将指针返回到本地数组,这非常糟糕。如果要返回指向数组的指针,请在func();中使用malloc动态分配它。
然后,您必须在调用方调用free()以释放不再需要的已分配内存
答案 4 :(得分:1)
在第一个示例中,您返回一个int,第二个示例中,您返回了一个指向char的指针。它们都以完全相同的方式返回,这只是了解堆栈以及如何返回值的问题。
即使在函数中声明了i
并将其分配在堆栈上,当函数返回时,它也会返回i
的值(基本上是复制的,因此当i
下降时离开堆栈仍返回i
的值。)
这与第二个示例中的char *
完全相同。它仍然是指向char的指针,并返回c
的“复制”值。但是,由于它是在堆栈上分配的,因此它指向的地址实际上是无效的。指针值本身并没有改变,但是指向的值却已经改变。
您必须动态分配它,以避免这种情况。
答案 5 :(得分:0)
函数的返回值由 copy 返回。在第一个示例中,您从函数中获取了整数变量的副本。在第二个步骤中,您将获得 char指针的副本,而不是字符串的副本。
该指针引用了具有自动存储的字符串数据,因此在函数返回后不再有效。该空间可供其他代码使用,并且可以对其进行很多修改-对其进行任何尝试均具有不确定的行为。
要点是,它是返回的指针,而不是字符串;在C语言中,字符串(通常是数组)不是一流的数据类型。
根据您的需求,有许多返回字符串数据的有效方法;例如,以下内容有效:
char* func()
{
static char c[] = "Hey there!";
return c;
}
因为这里尽管局部变量超出范围,但是静态数据不会被破坏或取消分配,并且对它的任何引用仍然有效。
另一种替代方法是将字符串嵌入到是一流数据类型的结构中:
typedef struct
{
char content[256] ;
} sString ;
sString func()
{
sString c = {"Hey there!"};
return c;
}
或更传统地将数据复制到调用者缓冲区:
char* func( char* buffer )
{
char c[] = "Hey there!";
strcpy( buffer, c ) ;
return buffer ;
}
在最后一个示例中,为清楚起见,我省略了代码以减轻缓冲区溢出的可能性,建议使用此类代码。