You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

代码说明

  1. 超长输入检测:通过判断fgets读取的缓冲区是否包含换行符,确定输入是否被截断。合法输入最长9字符,加上换行共10字符,Buffer[11]可完整容纳,因此无换行符则说明输入超长。
  2. clear_stdin函数:封装清空输入缓冲区的逻辑,替代未定义的fflush(stdin),保证跨平台兼容性。
  3. count_spaces修正:接收传入的缓冲区指针,用strlen获取实际输入长度,避免统计未使用的缓冲区空间。
  4. 精准格式校验:依次检查固定前缀、空格数量、字母范围、数字合法性,逻辑清晰无冗余。

内容的提问来源于stack exchange,提问作者HyperCoderSuperion

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 22:07:46