scanf失败了为什么?

时间:2010-10-25 15:24:34

标签: c scanf


当我写这篇文章时,编译并运行:

int x;   
scanf ("%d", &x);  
while (x!=4) {  
    scanf ("%d", &x);  
}

当插入char或小于4的双数时,它进入一个无限循环 当插入大于4的double时,它会终止 有什么解释吗?

4 个答案:

答案 0 :(得分:11)

来自C language standard (n1256)

7.19.6.2 fscanf功能
...
4 fscanf函数依次执行格式的每个指令。如果指令失败,如下所述,函数返回。故障被描述为输入故障(由于出现编码错误或输入字符不可用)或匹配 失败(由于输入不当) ...
7作为转换规范的指令定义了一组匹配的输入序列,如下面针对每个说明符所述。转换规范按以下步骤执行:

8跳过输入的空白​​字符(由isspace函数指定),除非 规范包括 [ c n 说明符。 250)

9除非规范包含 n 说明符,否则将从流中读取输入项。输入项被定义为输入字符的最长序列,其不超过任何指定的字段宽度,并且是匹配输入序列的前缀,或者是匹配输入序列的前缀。 251)第一个字符,如果有的话,输入项目仍然未读。如果输入项的长度为零,则指令的执行失败;除非文件结束,编码错误或读取错误阻止了流的输入,否则此条件是匹配失败,在这种情况下,它是输入失败。

10除了说明符的情况外,输入项(或者,在%n 指令的情况下,输入字符数)转换为类型适合转换说明符。 如果输入项不是匹配序列,则指令的执行失败:此条件是匹配失败。除非用*指示赋值抑制,否则转换的结果将放在对象中由格式参数后面的第一个参数指向尚未收到转换结果。如果此对象没有适当的类型,或者无法在对象中表示转换结果,则行为未定义。

第10段中添加了重点。%d转换说明符期望输入文本格式化为十进制整数。如果不是,则转换失败,导致转换失败的字符将保留在输入流中。使用scanf()转化说明符进一步调用%d会阻塞相同的字符。

scanf()返回成功作业的数量;您需要检查此结果以查看转换是否成功,如下所示:

int x = 0;
while (x != 4)
{
  int result = scanf("%d", &x);
  if (result != 1)
  {
    printf("Last call to scanf() failed; exiting\n");
    break;
  }
}

不幸的是,您仍然在输入流中卡住了错误的输入。有许多策略可以解决这个问题。您可以使用getchar删除有问题的字符,然后重试:

while (x != 4)
{
  int tmp;
  if (scanf("%d", &tmp) == 0)
    getchar();
  else
    x = tmp;
}

或者您可以尝试阅读下一个换行符,假设所有剩余的输入都是b0rked:

while (x != 4)
{
  int tmp;
  if (scanf("%d", &tmp) == 0)
    while (getchar() != '\n')
      ;
  else
    x = tmp;
}

或者您可以尝试将输入读作 text 并使用strtol()转换为整数(我的首选技术):

char input[SOME_SIZE];
int x = 0;
...
while (x != 4)
{
  if (fgets(input, sizeof input, stdin))
  {
    char *check;
    int tmp = (int) strtol(input, &check, 10);
    if (!isspace(*check) && *check != 0)
    {
      printf("%s is not a valid integer: try again\n", input);
    }
    else
    {
      x = tmp;
    }
  }
  else
  {
    printf("Read error on standard input\n");
    break;
  }
}

这是更多的工作,但它允许您在分配给x之前捕获错误的输入

答案 1 :(得分:6)

您不会检查scanf是否实际成功,因此您会遇到错误。对于每个循环,scanf将尝试读取并失败。

scanf返回成功读取的项目数,因此将循环修改为此类while (x!=4) { if (scanf("%d",&x) != 1) break; }

答案 2 :(得分:5)

当scanf停止在输入流中的特定位置扫描时,它将永远不会推进流,因此下一个scanf将再次尝试相同的错误......再次......再次

input: 42 23 foo ...
scanf: ^
x      42
scanf:   ^
x      23
scanf:      ^
x      unusable
scanf:       ^
x      unusable
scanf:       ^
x      unusable
scanf:       ^
x      unusable
scanf:       ^
x      unusable

答案 3 :(得分:-2)

传递给scanf的%d告诉它解析一个int。当你输入一个double时,它既不能解析也不会将解析后的数据存储在x变量中,因为double在32位机器上使用64位而int只是32位,从而引发了Seg。错误或随机的附带影响。