C语言多输入场景下如何避免缓冲区溢出且不干扰其他变量?
为什么%16s会导致溢出?
你定义的char word1[16]和word2[16]是16字节的数组,但C语言的字符串必须以\0(空字符)结尾标记结束。scanf("%16s", word)会读取最多16个非空白字符,然后自动在末尾添加\0——这就需要17个字节的空间,直接超出了数组的16字节容量,触发缓冲区溢出。
在你的失败示例里,第二个输入超长时,word2的溢出会覆盖相邻内存区域(刚好是word1的存储位置),把word1的起始位置写成了\0,所以输出时word1显示为空。而第一个输入超长时,word1的溢出可能没覆盖到word2的关键区域,所以看起来正常,但本质上还是存在溢出风险,只是没表现出明显异常。
可行的修复方法
方法1:限制读取长度为数组容量减1
这是你已经用到的方法,把%16s改成%15s,这样scanf最多读15个字符,加上自动添加的\0刚好占16字节,不会溢出。
方法2:使用fgets替代scanf
fgets可以指定最大读取长度(包含\0),安全性更高:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { printf("Enter a word: "); char word1[16]; // fgets读取最多15个字符+末尾的\0,同时会读取换行符 fgets(word1, sizeof(word1), stdin); // 去掉换行符(如果存在) word1[strcspn(word1, "\n")] = '\0'; printf("Enter another word: "); char word2[16]; fgets(word2, sizeof(word2), stdin); word2[strcspn(word2, "\n")] = '\0'; printf("Your first input: %s\n", word1); printf("Your second input: %s\n", word2); return 0; }
strcspn会找到换行符的位置,直接替换成\0,处理掉fgets读取的换行符。
方法3:用scanf读取后清理剩余输入
如果坚持用scanf,可以在每次读取后清空输入缓冲区里的剩余字符,避免影响下一次输入:
#include <stdio.h> #include <stdlib.h> int main() { printf("Enter a word: "); char word1[16]; scanf("%15s", word1); // 清理缓冲区剩余字符 int c; while ((c = getchar()) != '\n' && c != EOF); printf("Enter another word: "); char word2[16]; scanf("%15s", word2); while ((c = getchar()) != '\n' && c != EOF); printf("Your first input: %s\n", word1); printf("Your second input: %s\n", word2); return 0; }
这个循环会把缓冲区里直到换行符的所有字符读完,确保下一次scanf不会读到上一次剩下的内容。
为什么不能用fflush(stdin)?
C标准明确规定,fflush只能用于输出流(比如stdout),对输入流(比如stdin)使用fflush是未定义行为——不同编译器表现完全不同:微软的MSVC可能会实现清空输入缓冲区,但GCC、Clang等编译器完全不支持,程序运行时可能出现莫名其妙的错误,移植性极差。所以永远不要用fflush(stdin)清理输入缓冲区。
给新手的提问建议
- 贴完整可复现的代码:像你这次一样贴完整代码非常好,别人能直接编译测试,快速定位问题。
- 明确环境信息:说明你用的编译器(比如GCC 12、MSVC 2022)、操作系统(Windows/Linux/macOS),不同环境的行为可能有差异。
- 给出具体的输入输出:比如你这次的工作/失败示例就很清晰,把输入内容、预期输出、实际输出都列出来,别人更容易理解问题。
- 说明已尝试的方法:比如你提到已经改成
%15s解决了问题,这能帮回答者跳过无效建议,直接针对你的疑问解答。 - 聚焦核心问题:把问题拆成几个明确的小问题(比如为什么
%16s不行、替代方案、fflush的问题),结构清晰,别人更容易针对性回答。
内容的提问来源于stack exchange,提问作者Daniel Uman

