c - 使用 scanf() 函数两次:在一种情况下有效,但在另一种情况下无效



我现在正在学习C编程的基础知识,并且正在练习scanf()函数。这个非常简单的程序接收一个数字和一个字母,并使用printf()函数来显示数字和字母。

如果我要求用户先输入数字,程序就会工作,即要求一个数字,要求一个字母,然后打印输入。如果我先要求字母,程序会要求一个字母,但随后不会要求数字。

我已经尝试了多种方法并对其进行了重新排序,但它似乎不起作用。

这有效:

#include<stdio.h>
void main(){
int number;
char letter;
printf("Enter letter...");
scanf("%c", &letter);
printf("Enter number....");
scanf("%d", &number);
printf("Number entered: %d and letter entered: %c.n", number, letter);
}

但是,这种组合不起作用:

#include<stdio.h>
void main(){
int number;
char letter;
printf("Enter number....");
scanf("%d", &number);
printf("Enter letter...");
scanf("%c", &letter);
printf("Number entered: %d and letter entered: %c.n", number, letter);
}

我得到的第一个程序的输出是:

Enter letter...a
Enter number....9
Number entered: 9 and letter entered: a.

哪个是正确的

但是第二种情况不起作用,我不明白为什么它不起作用 - 跳过"输入字母"部分

输出为

Enter number....9
Enter letter...Number entered: 9 and letter entered: 
.

上下文:在上面的例子中,我输入了"a"作为字母,"9"表示数字。

事实证明,%d%c之间存在惊人的差异。 除了%d扫描可能多个数字而%c只扫描一个字符之外,令人惊讶的区别是%d跳过任何前导空格,而%c则不会

当你使用scanf来读取用户输入时,还有另一个容易被忽视的问题,那就是,当用户按ENTER键输入某些内容时,所有那些换行符(n字符)会发生什么?

所以这是发生的事情。 您的第一个程序有

printf("Enter letter...");
scanf("%c", &letter);
printf("Enter number....");
scanf("%d", &number);

用户键入一个字母、ENTER 和数字和 ENTER。 第一个scanf电话读了这封信,没有别的。n保留在输入流中。 然后第二个scanf调用,带有%d,跳过n(因为n是空格)并读取号码,就像您想要的那样。

但是在你的第二个程序中,你的输入是其他顺序的,像这样:

printf("Enter number....");
scanf("%d", &number);
printf("Enter letter...");
scanf("%c", &letter);

现在,用户键入一个号码并按 ENTER,第一个scanf调用读取该号码并将n留在输入流上。 但是在第二个scanf调用中,%c不会跳过空格,因此它读取的"字母"是n字符。

在这种情况下,解决方案是显式强制%c默认不执行的空格跳过。 关于scanf的另一个鲜为人知的事实是,格式字符串中的空格并不意味着"完全匹配一个空格字符",而是意味着"匹配任意数量的空格字符"。 因此,如果您将第二个程序更改为:

printf("Enter number....");
scanf("%d", &number);
printf("Enter letter...");
scanf(" %c", &letter);

现在,第二次scanf呼叫中" %c"中的空格字符将跳过用户键入号码后剩余的n,第二次scanf呼叫应读取它应该读取的字母。

最后,一点社论。 如果你认为这是一个奇怪的情况,如果你认为%c的工作方式的例外有点奇怪,如果你认为阅读一个数字后跟一个字母不应该这么难,如果你认为我对正在发生的事情的解释比它应该的要长得多,也更复杂——你是对的。scanf是 C 标准库中比较丑陋的函数之一。 我不知道有哪个C程序员用它来做任何事情——我不相信我曾经用过它。 实际上,它的唯一用途是让C程序员将数据放入他们的第一个程序中,直到他们学习其他更好的方法来执行该任务,这些方法不涉及scanf

所以我给你的建议是,不要花太多时间试图让scanf工作,或者了解它的所有其他弱点。 (它有很多。 一旦你感到舒服,就开始学习其他更好的输入方法,并把scanf永远舒适地抛在脑后。

试试这个

#include <stdio.h>
int main(void) {
int number;
char letter;
printf("Enter letter...");
scanf("%s", &letter);
printf("Enter number....");
scanf("%d", &number);
printf("Number entered: %d and letter entered: %c.n", number, letter);
return 0;
}

如果将%c更改为%s,则会得到正确的输出。

%c之前添加一个空格。因此,请更改以下内容:

scanf("%c", &letter);

对此:

scanf(" %c", &letter);

正如我在使用 scanf 时谨慎写的那样,这将使 scanf 吃掉空格和特殊字符(否则它会将它们视为输入)。

在这里,它将消耗换行符,换句话说,输入输入后按 Enter !

确切地说,在您的示例中,请考虑用户(在本例中为您)执行的操作:

  • 您键入 9
  • 你按回车键
  • 您键入"a">
  • 你按回车键

现在,当您输入某些内容时,在这种情况下,它将从键盘输入内容,这将进入标准输入缓冲区,在那里它将耐心等待被读取。

在这里,scanf("%d", &number);会来读一个数字。它在 STDIN 缓冲区的第一个单元格中找到 9,读取它,从而将其从缓冲区中删除。

现在,scanf("%c", &letter);来了,它读了一个字符。它找到换行符,这是你按下的第一个 Enter 键,函数现在很高兴 - 它被告知读取一个字符,这正是它所做的。现在换行符从缓冲区中删除(现在剩下的是"a"和一个换行符 - 这两个不会被读取,因为没有其他函数调用。

那么如果我改写scanf(" %c", &letter);会有什么变化呢?

第一个扫描仍将读取数字 9,缓冲区现在将有一个换行符、"a"字符和另一个换行符。

现在调用 scanf("%c", &letter);' ,它会在 STDIN 缓冲区中搜索要读取的字符,只是现在它将首先使用找到的任何特殊字符。

所以它进入缓冲区,它首先遇到换行符,它消耗它。

然后,它将遇到'a',这不是一个特殊字符,因此它将正常读取,并存储在scanf中传递的变量中。

最后一个换行符将保留在 STDIN 缓冲区中,保持不变且不可见,直到程序终止并且缓冲区被解除分配。


提示:您可能打算写int main(void),而不是void main()。阅读更多 main() 应该在 C 和 C++ 中返回什么?

按以下方式指定scanf

scanf("%c", &letter);

不会跳过空格,例如,当用户按 Enter 输入以前的数据时,可以读取存储在输入缓冲区中的换行符。

改用

scanf(" %c", &letter);
^^^

以跳过空格。

来自 C 标准(7.21.6.2fscanf函数)

8 输入空格字符(由 isspace 函数指定) 被跳过,除非规范包含 [、C或 N 说明符。

5 由空格字符组成的指令由 读取输入直到第一个非空格字符(保留 未读),或直到无法读取更多字符。

注意,根据C标准,没有参数的函数main应该声明为类似

int main(void)

来自 C 标准(5.1.2.2.1 程序启动)

1 程序启动时调用的函数名为 main。这 实现没有声明此函数的原型。它应该是 使用 int 返回类型定义且不带参数:

int main(void) { /* ... */ }

相关内容

最新更新