好吧,恕我直言,这是因为scanf 不会强制您限制输入大小,但fgets 会。
如果您阅读文档并正确使用scanf,这里确实没有太大区别:
char line[256];
scanf("%255[^\n]%*c",line); // %*c to remove trailing \n
fgets(line, 256, stdin)
请注意,我从 scanf 格式字符串中删除了前导空格。
我稍后再讨论。
这两种情况都确保我们不会阅读过多。
但是,从安全的角度来看,我们需要思考:
-
fgets强制你指定大小
- 在
scanf 上你需要记住设置array_capacity - 1
fgets 为你做这件事,所以你实际上传递了容量(或更少)
- “伙计,这种格式到底是什么意思??”
很容易忘记scanf的细节,当你与一个拥有不同编程背景的庞大团队打交道时,有些人可能会更麻烦地编写包含这些细节的代码,甚至可能不理解格式字符串。
所以一般来说使用fgets比较安全。
现在,关于我删除的前导空格。
当您使用 fgets 时,您将无法在输入前忽略 whitespace 字符,因此我必须删除该空格以使两个调用具有几乎相同的结果。
我想我们不能真的说一种方式比另一种方式“更好”,只有fgets 更具可读性,并确保您记住传递大小。您还可以通过将scanf 调用封装到正确构建格式字符串的输入读取函数中来实现这一点。这还可以让您在阅读前跳过前导 whitespace 字符。
编辑
很清楚,我的观点是,从安全的角度来看(如“不在字符数组之外写入”),这两种解决方案都是有效的,因为您可以限制它们读取的字符数。
我显示的代码纯粹是为了表明您可以将其限制为一定的大小,而不是它们具有完全相同的效果。
正如 Andrew Henle 所说,如果用户提供的字符多于我们说要读取的长度,%*c 确实会丢弃一个输入字符。
但是话又说回来,这不是我的意思,也不是问题,恕我直言。
我只是把它放在那里更接近fgets 所做的事情,因为如果输入不大于您尝试读取的数量,fgets 会从缓冲区中删除\n。
据我所知,这个问题是关于 scanf 与 fgets 的问题,并没有特别的意图。
至少有一个问题没有被描述。
当然,您需要考虑许多额外的事情,具体取决于应用程序需要执行的操作。
fgets 的一些注意事项:
- 如果您的输入长度为
size - 1,\n 将留在缓冲区中
(这会弄乱下一个字符输入...)
- 如果您的输入长度为
< size,则\n 将插入到字符串中
(您需要删除它,因为您可能不需要它)
- 如果您的输入长度为
> size,则剩余字符将留在缓冲区中
(你也需要以某种方式对待它)
- 如果更改数组长度,则需要在调用时更新大小
- 在提取数据之前不能忽略空格
scanf 的一些注意事项:
- 您需要决定是否可以安全地执行
%*c
(老实说,最好不要使用%*c,而只是在读取字符时在格式字符串上有一个前导空格,因为您已经在问题的代码上使用了)
- 您需要记住在宽度说明符上设置
capacity - 1
- 如果更改数组长度,则需要更新
width 说明符
- 可以在提取数据之前忽略空格
- 大多数人不知道比
%s、%d 等更复杂的格式说明符,因此团队中的其他人可能很难理解代码
- 如果输入长度与您指定的
width 匹配,则\n 将留在缓冲区中
- 如果输入长度大于
width,剩余字符也会留在输入缓冲区中
无论如何,可能还有更多场景需要考虑,其中许多取决于您需要处理的具体情况。
这两种功能各有利弊。
我不认为它们中的任何一个本质上都是“坏的”,它们都要求用户正确使用它们并自己处理一些错误,但是,fgets 当然具有强制您提供长度和更具可读性的优势.
我希望我的观点现在更清楚了。