我现在正在学习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) { /* ... */ }