为了账号安全,请及时绑定邮箱和手机立即绑定

scanf为何失败?

/ 猿问

scanf为何失败?

C
手掌心 2019-12-06 12:52:09

当我编写此代码时,编译并运行:


int x;   

scanf ("%d", &x);  

while (x!=4) {  

    scanf ("%d", &x);  

}

当插入char或小于4的双数时,它将进入无限循环。

当插入大于4的double时终止。

有什么解释吗?


查看完整描述

3 回答

?
繁星淼淼

根据C语言标准(n1256):


7.19.6.2 fscanf函数

...

4 fscanf函数依次执行格式的每个指令。如果指令失败,如下所述,函数将返回。失败描述为输入失败(由于发生编码错误或输入字符不可用)或匹配失败(由于不适当的输入)。

...

7作为转换规范的指令定义了一组匹配的输入序列,如下文针对每个说明符所述。转换说明将按照以下步骤执行:


8除非输入规范中包含[,c或n说明符,否则将跳过输入的空白字符(由isspace函数指定)。250)


9从流中读取一个输入项,除非规范包括n个说明符。输入项定义为最长的输入字符序列,该序列不超过任何指定的字段宽度,并且是匹配输入序列的前缀,或者是匹配输入序列的前缀。251)输入项之后的第一个字符(如果有)保持未读状态。如果输入项的长度为零,则该指令的执行失败;否则,该指令的执行失败。除非文件末尾,编码错误或读取错误阻止了来自流的输入,否则此条件是匹配失败,在这种情况下,这是输入失败。


10除非是%说明符,否则输入项(或者,如果是%n指令,即输入字符数)转换为适合转换说明符的类型。如果输入项不是匹配序列,则指令的执行失败:此条件是匹配失败。除非用*表示禁止分配,否则转换结果将放置在尚未收到转换结果的format参数后面的第一个参数指向的对象中。如果该对象没有适当的类型,或者无法在对象中表示转换结果,则该行为是不确定的。

在第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;

}

或者,您可以尝试将输入读取为文本,然后使用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。


查看完整回答
反对 回复 2019-12-06
?
桃花长相依

您无需检查scanf实际是否成功,因此会陷入错误。对于每个循环,scanf都会尝试读取并失败。

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


查看完整回答
反对 回复 2019-12-06
?
慕婉清6462132

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


输入:42 23 foo ...

scanf:^

x 42

scanf:^

x 23

scanf:^

x无法使用

scanf:^

x无法使用

scanf:^

x无法使用

scanf:^

x无法使用

scanf:^

x无法使用


查看完整回答
反对 回复 2019-12-06

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信