#include<stdio.h>
#include<stdlib.h>
int main()
{
char* name;
name=(char* )malloc(sizeof(char));
printf("Enter a name: ");
scanf("%s",name);
printf("%s",name);
return 0;
}
当我为单个字符分配内存时,上面的代码完美地存储了整个字符串。这怎么可能?
答案 0 :(得分:2)
你有undefined behavior。非常scared。阅读Lattner's blog on UB。
printf("%s",name);
当name
是一个真正的字符串(这是一个以零字节结尾的合法且有效的内存区域)时,这是有效的。在您的程序中它不是,您正在访问malloc
- ed区域之后的至少一个字节。你有一个buffer overflow。
当单个字符区域是有效字符串时,唯一的情况是单个char
包含零字节,因此该字符串为空。
上面的代码完美地存储了整个字符串
你运气不好(BTW,它不是“完美”)。它可能更糟(例如宇宙崩溃)并且可能更好(例如一些segmentation violation,nasal demons,......)。这就是你应该害怕UB的原因。
(为了解释您的特定计算机上发生的情况,您需要深入了解实现细节,例如研究您的操作系统,编译器,指令集,编译器生成的汇编代码等等...... ;你不想在这方面花费多年的工作,即使你这样做也留在UB)
您应该编译所有警告和调试信息(gcc -Wall -Wextra -g
)并使用调试器gdb
,valgrind并确保您的程序正在处理的每个内存位置都有效。< / p>
BTW malloc
可能会失败,您应该对此进行测试,sizeof(char)
按定义1.您应该尝试
char* name = malloc(80);
if (!name) { perror("malloc"); exit(EXIT_FAILURE); };
请同时阅读您正在使用的所有功能的文档。从printf和malloc以及scanf开始。请注意,您使用scanf
是危险的。最好使用printf
结束\n
控制字符串或适当使用fflush(因为stdout通常是行缓冲的)。还下载并研究n1570编程语言的规范C11。
在某些系统上,您拥有的不仅仅是C11标准所保证的。例如,POSIX有getline
,您可以像here一样使用它。如果你没有POSIX系统,也要考虑fgets(在C11标准中)(那么你需要复杂的技巧来读取任意长行,或者指定和文档你的程序如果使用malloc(80)
),则只能处理最多79个字节的行。
代码中avoid arbitrary limits的礼貌很好;当然你的计算机仍然有局限性(你可能无法处理一千亿字节的行)。
请注意character encoding(另请参阅this)。立即使用2017年的UTF-8 everywhere {例如help或libunistring {},但请记住,glib字符可以跨越几个字节(即char
- s)。
答案 1 :(得分:1)
您只为一个字符分配了内存,这足以存储终止空字节。你需要分配更多。
例如:
char *name = malloc(128); /* allocates 128 bytes */
printf("Enter a name: ");
if (fgets(name, 128, stdin) == NULL) {
/* input failure */
}
请注意scanf()
is notorious for reading input。更喜欢fgets()
。另请注意,如果您有空间可能要删除,fgets()
会在换行符中读取。
另见:Why does some code carefully cast the values returned by malloc to the pointer type being allocated?
答案 2 :(得分:1)
您只能将一个字符串安全地存储到该数组中,并且该字符串的长度为零。
也许,对于你的财富,这段代码似乎可以在你的余生中发挥作用,而且你永远不会听到任何关于它的抱怨......但是 不可能的。
输入更多字符;用几KB将键盘压缩,我打赌你会开始看到分段错误。很高兴我告诉你这个,因为我可能为你节省了很多时间的调试!
话虽如此,我无法提供100%的保证;这只是一个有根据的猜测。也许您的系统没有段错误。也许你的系统让黑客绕过了安全性。谁知道?行为未定义。
由于编码不安全,我本可以为您节省诉讼,但除了标准编程语言给出的定义之外,我们无法对未定义行为进行定义,是:非便携式和不稳定。
现在应该清楚避免这种情况的原因。