【问题标题】:How to prevent input using scanf from overflowing?如何防止使用scanf的输入溢出?
【发布时间】:2014-02-09 21:43:37
【问题描述】:

我对 C 比较陌生,想知道如何防止输入溢出...

例如,我有:

scanf("%d", &a);

其中 a 是一个整数。

那么我可以做些什么来防止有人输入大于最大整数的数字?由于我正在处理的问题的限制,您必须使用scanf。如何限制输入?

【问题讨论】:

  • 使用scanf读取一个字符串,然后自己解析?
  • 理论上您可以查看 errno 是否为 ERANGE 以表示超出范围的错误。但是,这不是为 scanf 指定的标准。请参阅linux.die.net/man/3/scanf ''C89 and C99 and POSIX.1-2001 standards do not specify the ERANGE error '' 换句话说,如果您需要读取整数同时还具有检查溢出的能力,则应该使用strtol
  • 如果您必须使用scanf,一种简单的方法是读取double,而不是使用scanf(范围更大),进行自己的范围检查,然后然后将结果转换为int(如果它符合您的条件)。只要您的有效整数范围不太大,这将不会丢失信息。
  • @Brandin:ANSI-C 规范确实说匹配数字序列,然后使用 strtol 函数进行转换。因此,如果该值长时间过大,您将得到一个 ERANGE。但是,如果它对于 int 来说太大但适合 long ,则尚不清楚会发生什么。

标签: c integer overflow scanf


【解决方案1】:

阻止用户输入非常具有挑战性。
没有神奇的手可以伸出手来阻止用户敲击键盘。

但代码可以限制它读取的内容。

  1. scanf() 很难限制。它可能不会在溢出时设置errno。代码可以将char 的数量限制为 9。这是第一步,但不能输入诸如“1000000000”或“00000000000000001”之类的值。

    // Assume INT_MAX = 2,147,483,647.
    scanf("%9d", &a);
    
  2. 迂腐的方法会使用fgetc()。一个unsigned 方法如下。 int 需要更多时间。

    unsigned a = 0;
    int ch = fgetc(stdin);
    while (isspace(ch)) {
      ch = fgetc(stdin);
    }
    while (isdigit(ch)) {
      ch -= '0';
      if (a >= UINTMAX/10 && (a > UINTMAX/10 || ch > UINTMAX%10)) {
        a = UINTMAX;
        break;  // overflow detected.
      }
      a = a*10 + ch;
      ch = fgetc(stdin);
    }
    ungetc(ch, stdin);  // Put back `ch` as it was not used.
    
  3. 但我更喜欢改变目标并简单地再次告诉用户,即使这意味着阅读更多字符。

    // 0:success or EOF
    int Read_int(const char *prompt, int *dest, int min, int max) {
      for (;;) {
        char buf[(sizeof(int)*3 + 3)*2];  // 2x generous buffer
        fputs(prompt, stdout);
        if (fgets(buf, sizeof buf, stdin) == NULL) {
          return EOF;
        }
        char *endptr;
        errno = 0;
        long l = strtol(buf, &endptr, 10);
        // no conversion or junk at the end ....
        if (buf == endptr || *endptr != '\n') {
          continue;
        }
        if (!errno && l >= min && l <= max) {
         *dest = (int) l;
         return 0; // success
        }
      }
    }  
    
    // Sample usage
    int a;
    Read_int("Enter an `int`\n", &a, INT_MIN, INT_MAX);
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-09
    • 1970-01-01
    • 2021-08-21
    • 2019-12-18
    • 2020-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多