C语言读取输入应选择fgets还是带长度限制的scanf?
C语言输入读取选型与fgets安全问题解答
为什么更推荐使用fgets而非带格式限制的scanf?
- 输入完整性与缓冲区残留处理更简单
fgets是专门为整行输入设计的函数,会读取到换行符或缓冲区满为止,且自动将换行符存入缓冲区,不存在输入残留问题。而即使使用%[^\n]之类的扫描集让scanf支持读取空白字符,scanf遇到换行符时会直接停止读取,换行符会残留在输入缓冲区中,后续的输入操作会直接读到这个残留的换行符,导致逻辑错误,需要额外编写代码清空缓冲区,非常容易遗漏。 - 容错性更高,排查成本更低
scanf强依赖格式串匹配,一旦输入内容和格式规则不匹配,会直接终止读取,未匹配的内容全部残留在缓冲区,后续所有输入操作都会得到不符合预期的结果,排查难度极高。而fgets不管输入内容是什么,都会按行读取,不存在匹配失败导致的级联错误。 - 长度限制更不容易出错
scanf格式串中写入的长度限制不包含字符串末尾的\0,比如%10s最多读取10个非空白字符,再自动追加1个\0,所以缓冲区大小至少要11字节,很多开发者容易忽略这个点,直接申请10字节的缓冲区就会触发溢出。而fgets的第二个参数直接传入缓冲区总大小即可,函数内部会自动预留\0的位置,最多读取参数值-1个字符,使用逻辑更符合直觉,出错概率极低。 - 维护成本更低
scanf的长度限制是硬编码在格式串中的,如果后续调整缓冲区大小,需要同步修改格式串中的长度值,很容易漏改。而fgets可以直接传sizeof(缓冲区名)作为长度参数,缓冲区大小变更时不需要额外修改调用参数。
fgets是否会引发缓冲区溢出?如何处理超长输入?
正确传参的前提下fgets不会触发缓冲区溢出,它的长度限制逻辑是标准库层面保证的,就算用户输入的字符数远大于缓冲区大小,也不会发生越界写入。
超长输入只会带来逻辑问题:fgets只会读取缓冲区大小减1的字符,剩下的半行内容会留在输入缓冲区中,下一次调用读取函数时会读到上一行的剩余内容,而不是新的输入行,处理方案如下:
- 每次调用
fgets完成后,先检查缓冲区末尾是否存在换行符\n- 如果存在换行符,说明整行已读取完成,直接把换行符替换为
\0即可正常使用 - 如果不存在换行符,说明输入超长,需要循环读取输入缓冲区的内容直到读到换行符或EOF,清空残留的半行内容,避免影响后续读取
- 如果存在换行符,说明整行已读取完成,直接把换行符替换为
- 参考实现代码:
#include <stdio.h> #include <string.h> #define BUF_SIZE 128 int main() { char buf[BUF_SIZE]; if (fgets(buf, sizeof(buf), stdin) == NULL) { // 读取失败,一般是遇到EOF或IO错误 return 1; } char *newline_pos = strchr(buf, '\n'); if (newline_pos != NULL) { // 整行读取完成,去掉换行符 *newline_pos = '\0'; } else { // 输入超长,清空残留内容 int c; while ((c = getchar()) != '\n' && c != EOF); // 可选:在这里提示用户输入过长 } return 0; }
内容的提问来源于stack exchange,提问作者Becker
相关产品推荐
相关产品推荐

