C语言使用fgets时如何安全处理超缓冲区长度的非法输入
问题分析与解决方案
你的核心问题是:当输入长度超过11字节缓冲区时,fgets只会截断读取前10个字符(加上终止符'\0'共11字节),现有代码仅在strlen(Buffer)>10时判定无效,但超长输入时fgets不会把换行符读入缓冲区,此时你虽清空了剩余输入,却未将这种情况标记为错误。此外代码还有几个隐含问题:fflush(stdin)是C标准未定义的行为、count_spaces错误使用全局数组的sizeof而非实际输入长度、格式检查逻辑冗余且不准确。
关键修复点
- 超长输入检测:当
fgets读取后,若缓冲区末尾无换行符且不是EOF(说明输入未读完),直接判定为无效输入。 - 替换
fflush(stdin):用循环读取字符直到换行或EOF来清空输入缓冲区,这是标准C中合法的方式。 - 修正
count_spaces:改为接收输入缓冲区参数,用strlen获取实际输入长度,避免依赖全局变量和错误的数组大小计算。 - 简化格式校验逻辑:直接针对
lunch XYZ的格式做精准检查,减少冗余判断。
修改后的完整代码
#include <stdio.h> #include <string.h> #include <ctype.h> #define MAX_BUFFER 11 char Buffer[MAX_BUFFER]; int count_spaces(const char *buffer) { int count = 0; size_t len = strlen(buffer); for (size_t i = 0; i < len; ++i) { if (buffer[i] == ' ') { count++; } } return count; } int lunch(const char *buffer) { size_t len = strlen(buffer); // 合法输入长度为8-9字符(含空格) if (len < 8 || len > 9) { printf("Invalid!\n"); return 0; } // 检查开头是否为固定的"lunch " if (strncmp(buffer, "lunch ", 6) != 0) { printf("Invalid!\n"); return 0; } // 空格数量必须为1 if (count_spaces(buffer) != 1) { printf("Invalid!\n"); return 0; } // 提取后缀部分并校验格式 const char *suffix = buffer + 6; // X必须是A-T大写字母 if (!isupper((unsigned char)suffix[0]) || suffix[0] < 'A' || suffix[0] > 'T') { printf("Invalid!\n"); return 0; } // Y、Z必须是0-9数字(长度9时校验两位,长度8时校验一位) if (len == 9) { if (!isdigit((unsigned char)suffix[1]) || !isdigit((unsigned char)suffix[2])) { printf("Invalid!\n"); return 0; } } else if (len == 8) { if (!isdigit((unsigned char)suffix[1])) { printf("Invalid!\n"); return 0; } } // 所有校验通过 printf("Valid command!\n"); return 1; } void clear_stdin() { int c; while ((c = getchar()) != '\n' && c != EOF) {} } int main() { while (fgets(Buffer, MAX_BUFFER, stdin) != NULL) { int valid_command_entered = 0; size_t buf_len = strlen(Buffer); int input_truncated = 0; // 检测输入是否被截断:缓冲区无换行符且未到EOF if (buf_len > 0 && Buffer[buf_len - 1] != '\n') { input_truncated = 1; clear_stdin(); // 清空剩余输入 } else { // 移除换行符 Buffer[buf_len - 1] = '\0'; buf_len--; } // 超长输入直接判定无效 if (input_truncated || buf_len > 9) { printf("Invalid!\n"); continue; } // 处理合法前缀的命令 if (strstr(Buffer, "lunch") != NULL) { valid_command_entered = lunch(Buffer); } // 未通过任何有效校验 if (!valid_command_entered) { printf("Invalid!\n"); } } return 0; }
代码说明
- 超长输入检测:通过判断
fgets读取的缓冲区是否包含换行符,确定输入是否被截断。合法输入最长9字符,加上换行共10字符,Buffer[11]可完整容纳,因此无换行符则说明输入超长。 clear_stdin函数:封装清空输入缓冲区的逻辑,替代未定义的fflush(stdin),保证跨平台兼容性。count_spaces修正:接收传入的缓冲区指针,用strlen获取实际输入长度,避免统计未使用的缓冲区空间。- 精准格式校验:依次检查固定前缀、空格数量、字母范围、数字合法性,逻辑清晰无冗余。
内容的提问来源于stack exchange,提问作者HyperCoderSuperion
相关产品推荐
相关产品推荐

