我对C字符串声明中的一些基础知识感到困惑。我尝试了以下代码,我发现了一些不同之处:
char* foo(){
char* str1="string";
char str2[7]="string";
char* str3=(char)malloc(sizeof(char)*7);
return str1;
/* OR: return str2; */
/* OR: return str3; */
}
void main() {
printf("%s",foo());
return 0;
}
我让foo()一次返回一个str1 / 2/3,并尝试在main中打印结果。 str2返回了一些奇怪的东西,但是str1和str3返回了实际的“字符串”。
1.现在,三个声明之间的区别是什么?我认为str2不起作用的原因是因为它被声明为局部变量,这是正确的吗?
2.那么str1怎么样?如果结果在foo()结束后仍然存在,那不会导致内存泄漏吗?
3.我只是想编写一个在C中返回一个字符串的函数,并将该函数返回的值用于其他东西,我应该使用上面的str声明吗?
提前致谢!
答案 0 :(得分:9)
char* str1="string";
这使str1
成为指针;它指向字符串文字的第一个字符。您应该将其定义为const
,因为您不允许修改字符串文字:
const char *str1 = "string";
...
char str2[7]="string";
这使str2
成为char
(不是指针)的数组,并将字符串文字的内容复制到其中。没有必要将其定义为const
;数组本身是可写的。您也可以省略大小,并由初始化程序确定:
char str2[] = "string";
然后sizeof str2 == 7
("string"
为6个字节,终止'\0'
为1个。)
此:
char* str3=(char)malloc(sizeof(char)*7);
写得不正确,它甚至不应该编译;至少,您应该收到编译器的警告。您正在将malloc()
的结果投射到char
类型。您应该将其转换为char*
:
char *str3 = (char*)malloc(sizeof(char) * 7);
但演员阵容是不必要的,在某些情况下可以掩盖错误;请参阅问题7.7并在comp.lang.c FAQ中跟随:
char *str3 = malloc(sizeof(char) * 7);
但是sizeof(char)
根据定义是1,所以你可以写:
char *str3 = malloc(7);
malloc()
分配内存,但它没有初始化它,所以如果你试图打印str3
指向的字符串,你会得到垃圾 - 甚至是如果分配的空间不包含终止空字符'\0'
,则运行时崩溃。您可以使用strcpy()
对其进行初始化,例如:
char *str3 = malloc(7);
if (str3 == NULL) {
fprintf(stderr, "malloc failed\n");
exit(EXIT_FAILURE);
}
strcpy(str3, "string");
您必须非常小心,您复制的数据不会超过分配的空间。 (不,`strncpy() is not the answer to this problem。)
void main()
不正确;它应该是int main(void)
。如果您的教科书告诉您使用void main()
找到更好的教科书;它的作者不太了解C.
对于您正在使用的任何库函数,您需要适当的#include
指令:<stdio.h>
printf()
,<stdlib.h>
exit()
和{malloc()
1}}和<string.h>
strcpy()
。每个函数的文档应告诉您要包含哪个标题。
我知道要吸收很多东西;不要期望马上理解它。
我提到comp.lang.c FAQ;它是一个很好的资源,特别是第6节,它讨论了数组和指针以及它们之间经常令人困惑的关系。
至于你的问题3,如何从C函数返回一个字符串,由于C进行内存分配的方式(实际上它留给自己管理),结果出乎意料地复杂得多。您无法安全地返回指向局部变量的指针,因为当函数返回时变量不再存在,使调用者留下悬空指针,因此返回str2
是危险的。返回字符串文字是可以的,因为它对应于整个程序执行时存在的匿名数组。您可以使用static
声明一个数组并返回指向它的指针,或者您可以使用malloc()
(这是最灵活的方法,但这意味着调用者需要free()
内存) ,或者您可以要求调用者将指针传递给您的函数将复制结果的缓冲区。
某些语言允许您构建字符串值并简单地从函数返回它。 C,正如您现在发现的那样,不是其中一种语言。
答案 1 :(得分:6)
char* str1="string";
这将创建一个指向文字字符串的指针,该字符串将位于.data
或.text
段上,并且始终可以访问。每当你做这样的事情时,一定要声明它const
,因为如果你试图修改它,可能会发生令人讨厌的事情。
char str2[7]="string";
这将在堆栈上创建一个本地缓冲区,其中包含文字字符串的副本。一旦函数返回它就变得不可用。这解释了你得到的奇怪结果。
char* str3=(char)malloc(sizeof(char)*7);
这将在堆上创建一个缓冲区(未初始化),直到您释放它为止。你必须释放它,否则你会得到内存泄漏。
答案 2 :(得分:0)
字符串文字 "string"
存储为具有静态范围的char
的7元素数组,这意味着它的内存在程序启动时分配并保持到程序终止。
声明
char *str1 = "string";
将字符串文字的地址分配给str1
。即使变量str1
在函数退出时不再存在,其值(文字"string"
的地址)在函数外部仍然有效。
声明
char str2[7] = "string";
将str2
声明为char
的数组,并将字符串文字的内容复制到其中。当函数退出时,str2
不再存在,其内容不再有意义。
声明
char *str3 = (char*) malloc(sizeof(char) * 7);
可以简化为
char *str3 = malloc(sizeof *str3 * 7);
分配一个未初始化的7字节内存块,并将其地址复制到str3
。当函数退出时,变量str3
不再存在,但它指向的内存仍然被分配。如上所述,此是内存泄漏,因为您不会在调用代码中保留指针的值。请注意,由于您没有将任何内容复制到此块,main
中的输出将是随机的。
此外,除非您的编译器文档明确列出void main()
作为main
函数的合法签名,否则请改用int main(void)
。