C语言中的puts和printf函数

5

我是C语言的新手。在使用 puts 和 printf 时遇到了一些问题。

char str[10];
printf("Input a string.\n");
gets(str);
printf("The string you input is: %s",str);

如果我输入的字符数超过10个,输出结果如下。
1ang:lab lang$ ./exercise 
Input a string.
warning: this program uses gets(), which is unsafe.
0123456789
Abort trap: 6

但是当我在printf语句末尾添加\n时,输出结果就会有所不同:printf("你输入的字符串是:%s\n",str);

1ang:lab lang$ ./exercise 
Input a string.
warning: this program uses gets(), which is unsafe.
0123456789
The string you input is: 0123456789
Abort trap: 6

它会先打印字符串,然后发生错误。有人能解释一下吗?


4
"gets()"已不建议使用,应使用"fgets()"代替。 - tigris
5
不,我们不会在这里解释UB。 - Martin James
我相信这是一个缓冲问题。在使用带有 %s 的那个 printf("\n") 后再尝试使用另一个 printf("\n") 。并且尽量避免使用 gets,因为你的 libc 建议不安全。 - Marandil
1
请注意,即使是“0123456789”,'char str[10]'也太小了。 - Martin James
2
未定义行为无法解释,它只是未定义的。请使用 fgets 而不是 gets - SKD
显示剩余3条评论
4个回答

7

你没有为字符串预留足够的空间。

请记住,字符数组需要多预留一个用于添加空字符(\0)的元素。因此str[10]的大小不够。

由于printf中的str参数不包含\0,所以它会导致缓冲区溢出并产生未定义结果。

解决这个问题的一种方法是考虑使用fgets。即使为测试字符串预留了足够的内存,您仍然可以轻松地提供更长的字符串来使程序崩溃。

此外,考虑将\n附加到printf字符串中:这个换行符刷新了输出缓冲区,有助于及时将内容写回控制台。


2
gets是不安全的,在你的代码中,我认为它会覆盖你的堆栈的一部分,可能是返回指针寄存器(我不确定名称)。
现在,为什么你在某些情况下有消息,而在其他情况下没有?
因为在printf中打印"\n"会强制刷新已经缓冲的内容。
在第一种情况下,崩溃发生在文本显示在控制台之前,但如果你使用调试器,将会看到它发生在同一点上。

1
你的编译器告诉你:“warning: this program usesgets(), which is unsafe.”,但你仍然继续。然后你读取了足够的数据来溢出你的数组,可能会破坏其他存储。
接下来发生的是未定义的行为,所以任何事情都可能发生。在你的情况下,似乎printf成功了,但是损坏导致后面的代码(例如从函数返回)崩溃。如果你刷新了stdout(如果stdout是终端,则隐式发生),那么你将看到输出。如果没有,当进程被销毁时,缓冲输出将丢失。

0

你所遇到的是未定义行为的一个很好的例子。如果超出数组缓冲区大小,发生了什么是没有定义的。正如其他人指出的那样,你真的不应该使用 gets。这个函数不是没有原因被认为是过时的。最好使用 fgets 或者 fscanf

我还建议你编写一个“安全”的输入函数,使用 scanf 的返回值。或者你可以使用 fgetssscanf 的组合来从输入中读取整行,并在之后使用 sscanf 操作读取的字符串,就像 this 答案中所示。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接