问题描述
我需要从 main 调用一个函数来确定一个字符串值。但是,据我所知,字符串不能采用 string_name = call_function()
形式。字符串的赋值必须是 strcpy(str1,str2)
的形式对吗?所以我想知道我在用 strcpy
作为变量名的 str1
做错了什么,而 str2
是另一个函数的返回字符串值。
到目前为止,这就是我所拥有的。
#include <stdio.h>
#include <string.h>
char *get_name(int num);
char *get_name(int num) {
char real_name[30];
if (num == 1)
strcpy(real_name,"Jake Peralta");
return real_name;
}
void main() {
char name[30];
int num;
num = 1;
strcpy(name,*get_name(num));
printf("%s",name);
}
我尝试过的:
-
get_name(num)
不使用指针(仍然不起作用)
p/s:这不是实际的代码。这只是我想要做的一个例子,实际代码更长,我已经确定这部分是错误的来源。
解决方法
您是对的,字符串不能通过 return
语句作为数组返回。当您传递或返回数组时,仅使用指向其第一个元素的指针。因此,函数 get_name()
返回一个指向本地定义的数组的指针,该数组具有自动存储(又名在堆栈上)。这是不正确的,因为这个数组一旦超出范围就会被丢弃,即:当函数返回时。
get_name()
可以通过多种方式为其调用者提供名称:
-
您可以传递目标数组及其长度:并让函数在数组中填充名称,小心避免写到数组末尾之外,但要确保它有一个空终止符:
char *get_name(char *dest,size_t size,int num) { if (num == 1) { snprintf(dest,size,"Jake Peralta"); } else { snprintf(dest,"John Doe"); } // return the destination pointer for convenience. return dest; } int main() { char name[30]; int num = 1; get_name(name,sizeof name,num); printf("%s\n",name); return 0; }
-
您可以在
get_name()
中分配内存并返回指向您复制字符串的已分配数组的指针。当不再使用该对象时,调用者有责任使用free()
释放该对象。char *get_name(int num) { if (num == 1) { return strdup("Jake Peralta"); } else { return strdup("John Doe"); } } int main() { int num = 1; char *name = get_name(num); printf("%s\n",name); free(name); return 0; }
-
您可以返回一个常量字符串,但只有在编译时所有名称都已知的情况下才能这样做。
const char *get_name(int num) { if (num == 1) { "Jake Peralta"; } else { "John Doe"; } } int main() { int num = 1; const char *name = get_name(num); printf("%s\n",name); return 0; }
您正在从 real_name
方法返回 get_name
的地址,该地址将在函数返回后超出范围。而是在堆上分配字符串的内存并返回其地址。此外,调用者需要释放在堆上分配的字符串内存以避免任何内存泄漏。
就像您说的,“但是,据我所知,字符串不能采用 string_name = call_function()
形式。”要了解这背后的逻辑,只需查看您的 get_name()
函数:
char *get_name(int num)
{
char real_name[30];
if (num==1)
strcpy(real_name,"Jake Peralta");
return real_name;
}
这里,您尝试返回 real_name
的起始地址,但是当 real_name
超出范围时(在这种情况下,当函数返回时)会被销毁。我认为有两种方法可以解决此问题。一种是添加应该保存返回值的字符串作为参数。在您的情况下,它将是:
void get_name(int num,char *dest)
{
char real_name[30];
if (num==1)
{
strcpy(real_name,"Jake Peralta");
strcpy(dest,real_name);
}
}
或者,现在完全避免使用 real_name
以缩短函数:
void get_name(int num,char *dest)
{
if (num==1)
strcpy(dest,"Jake Peralta");
}
另一种方式是在堆上分配返回值,但我不建议这样做;您必须跟踪每个分配的字符串并最终释放所有字符串。不过,在您的情况下,情况如下所示:
char *get_name(int num,char *dest)
{
char *real_name = calloc(30,sizeof(char)); // Using calloc to avoid returning an uninitialized string if num is not 1
if (num==1)
strcpy(real_name,"Jake Peralta";
return real_name;
}
顺便说一下,我将您的 strcpy
保留在这里,但可能希望将来避免使用它,因为它会导致缓冲区溢出。 Here's a post with useful answers as to why it's bad。事实上,即使 strncpy
也不是完全安全的(请参阅 here(感谢 @chqrlie 提供链接))。 @chqrlie 在他自己的回答中使用 snprintf
提供了一种干净且安全的替代方案,我建议您使用它。