【问题标题】:C: Scanf Ignored in Type MismatchC: Scanf 在类型不匹配中被忽略
【发布时间】:2021-06-09 11:35:32
【问题描述】:

是的,SO 上已经存在类似的问题,scanf() 在“解释”回车键之前不会等待用户输入,但我的问题是这个问题只发生在扫描时——即@987654322 @(前面有一个空格)——读取一个字符串而不是另一个长数字。如果此摘要看起来令人费解,请在下方查看详细信息。

我在 C 中有以下程序:

#include <stdio.h>
#include <stdbool.h>

int main(void)
{
    long int cardnum, tmp;
    int num_of_digits;
    bool validate; // valid value
    LOOP:do
    {
        printf("Number: ");
        scanf(" %ld", &cardnum);

        tmp = cardnum, num_of_digits = 0;
        while (tmp != 0)
        {
            tmp /= 10;
            ++num_of_digits;
        }

        if (num_of_digits != 16)
        {
            printf("INVALID\n");
            goto LOOP;
        }

        validate = false;
        // ... (validate will be processed here, and there will be a case where "validate" will be true)
    }
    while (validate != true);
}

输入:

  • 4003600000000014:有效,长度为16
  • 400360000000001:打印INVALID,长度为15
  • asdf:无限循环(请注意并非所有无效输入都会导致此错误!
Number: INVALID
Number: INVALID
Number: INVALID
...

我在其他地方读到scanf 忽略了最后一行缓冲区(或其他内容),但在我的情况下,它只发生在scanf 没有从用户那里收到正确的输入类型时。有人可以帮忙吗?

提前致谢。

【问题讨论】:

  • 那么你想要asdf上发生什么? goto LOOP; 这太可怕了,不要使用 goto。只需continue
  • 格式说明符之前的那个空格对我来说从来没有用过,所以它卡在标准输入的换行符上,更简单的方法是使用getchar() 将其从那里取出,更好的做法是使用fgets 阅读并将其转换为int
  • @KamilCuk 如果输入了asdf,我想让用户再次输入。 goto 的目的是不进入以下流程以进行优化。如果有一个功能相同但“更好”的功能,请告诉我
  • @VladfromMoscow 编辑问题,感谢您的关注
  • @LEF,格式字符串中的文字空格字符匹配输入中相应点出现的空格,包括没有空格。但是,大多数转换说明符,包括%d,无论如何都会忽略任何前导空格,因此格式" %ld""%ld" 匹配的输入完全相同。

标签: c loops scanf do-while


【解决方案1】:

并非所有无效输入都会导致此错误!)

如果您输入的不是数字,那么scanf 将失败。所以检查它是否无法扫描一个数字,如果扫描失败,然后忽略输入直到行尾,例如。注意 - 你也应该处理EOF

if (scanf(...) != 1) {
    int c;
    // ignore input
    while ((c = getchar()) != EOF) {
        // until a newline!
        if (c == '\n') {
            break;
        }
    }
    // when eof
    if (c == EOF) {
        // then exit our program with a failure!
        printf("ERROR: End of input!");
        exit(-1);
    }
    // if not eof, means user is ready to enter yet another line
    printf("you inputted an invalid line - please input a line consisting of digits only!");
    continue;
}

返回值说明见cppreference scanf


LOOP:do {goto LOOP; 看起来是个可怕的想法——不要在那里使用 goto,看起来很混乱。相反,您更愿意重组您的代码,这样您就不必使用 goto。例如:

for (;;) {
    if (some_check) {
         continue;
    }
    if (some_another_check) {
         continue;
    }
    // if we get here, means all checks succeeded - so we need to break
    break;
}

goto 有很好的用途,而且它恰好被使用 - 参见前文。 https://www.kernel.org/doc/html/v4.10/process/coding-style.html#centralized-exiting-of-functions.

【讨论】:

  • 我能知道你用什么替换了scanf(" %ld", &amp;cardnum); 吗?我有点难以理解你在这里想要做什么(尤其是if (scanf(...) != 1) {
  • if (scanf(" %ld", &amp;cardnum) != 1) { - 我使用 .. 作为占位符。
  • != 1 是什么意思?
  • 在这里您可能会找到一些可能向您介绍 C 编程语言的 C 书籍:stackoverflow.com/questions/562303/…
【解决方案2】:

如果您输入的不是整数,那么您需要从输入缓冲区中删除无效输入。

这是一个演示程序。

#include <stdio.h>

int main(void) 
{
    long int x;
    int valid;
    
    do
    {
        if ( !( valid = ( scanf( " %ld", &x ) == 1 ) ) && valid != EOF )
        {
            scanf( "%*[^\n]%*c");
        }
    } while ( !valid );
    
    printf( "x = %ld\n", x );
    
    return 0;
}

控制台输出可能看起来像

A
12
x = 12

请注意,使用goto 语句是一种糟糕的编程风格。相反,您可以使用我的程序中显示的 do-while 语句。那其实就是你程序中的goto语句是多余的。

【讨论】:

    猜你喜欢
    • 2012-07-10
    • 2013-03-14
    • 2013-10-05
    • 1970-01-01
    • 2022-01-04
    • 1970-01-01
    • 2021-10-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多