对于计算机程序而言,输入输出是非常核心的功能。可以说,程序经常得通过输入来获取数据,然后经过计算之后再通过输出呈现给用户。在C语言中,有一个非常重要的输入函数,那就是scanf函数。正确地使用scanf,对于C语言程序员来说非常重要,因为它能够帮助我们有效地获取并处理用户输入,减少输入错误。本文将以“”为主题,分享一些关于scanf的使用技巧。
一、什么是scanf函数?
scanf是C语言中的一个输入函数,在stdio.h库中定义。其作用是从标准输入(通常是键盘)读取数据,并将其存储到指定的变量中。scanf函数支持多种数据类型的输入,包括整数、浮点数、字符、字符串等等。该函数的原型如下:
int scanf(const char *format, …);
其中,第一个参数为格式字符串,后面的“…”表示可变参数列表,它们是我们要读取的变量地址。
二、scanf函数格式字符串
格式字符串是scanf函数中最重要的一部分,因为它定义了输入数据的类型和格式。下面是一些格式字符串的示例和解释。
1. %d 读取十进制整数
%d是框架字符串中最常用的占位符,用于读取十进制整数。可以使用%d读取整数型数值,如下所示:
```c
int num;
scanf("%d", &num);
```
2. %f 读取浮点数
%f是用于读取浮点数的占位符,可以读取单精度浮点数,双精度浮点数和长双精度浮点数,如下所示:
```c
float num;
scanf("%f", &num);
```
3. %c 读取一个字符
%c占位符可以帮助读取字符。注意,当输入字符并按下回车时,scanf将读取回车,并将其视为一个字符。因此,可以使用以下方式来清除缓冲区中的回车符:
```c
char c;
scanf(" %c", &c);
```
4. %s 读取字符串
%s用于读取字符串,将输入读取到一个字符串变量中。
```c
char str[100];
scanf("%s", str);
```
需要注意的是,如果想让scanf读取整行输入,应该使用%[^\n],如下所示:
```c
char str[100];
scanf("%[^\n]", str);
```
这将读取一个含有空白字符的输出行,直到遇到换行符(\n)结束。
5. %u 读取无符号整数
%u用于读取无符号整数。
```c
unsigned int num;
scanf("%u", &num);
```
6. %x、%X 读取十六进制整数
%x用于读取十六进制数,小写字母a-f,%X用于读取大写字母A-F的十六进制数。
```c
int num;
scanf("%x", &num); //读取十六进制数
scanf("%X", &num); //读取十六进制数
```
三、scanf函数的返回值
scanf函数的返回值表示成功读取数据的个数。如果返回值小于我们想读取的数据个数,那么说明发生了一些错误。例如:
```c
int num1, num2;
int result = scanf("%d%d", &num1, &num2);
if(result < 2){
printf("输入错误\n");
}
```
在以上代码中,我们要读取两个整数,但是如果用户输入的不足两个,那么scanf函数就会返回1或0,告诉我们输入有误。该程序会提示用户输入错误信息。
四、scanf函数中的格式化输入错误
在使用scanf函数时,最常见的错误之一是格式化输入错误。这意思是,如果scanf所读取的数据与变量的类型不符,可能会导致运行时错误。例如,如果输入一个字符串到一个整型变量中,那么可能会导致程序出现运行时错误。
```c
int num;
scanf("%d", num);
```
在以上代码中,我们忘记使用&符号,scanf将尝试将输入的值存储到内存中的另一个位置,可能会导致运行时错误。
为了避免此类错误,我们应该善用编译器。例如,GCC编译器默认启用-Wformat选项,该选项可发现包含格式化输入错误的程序。
五、批量输入
当需要输入大量数据时,一个一个地键入显然是不现实的。因此,我们可以使用循环结构批量读取数据。
例如,下面的程序会从stdin中读取n个整数,并将它们的和作为输入的函数。
```c
#include
int main()
{
int n, sum = 0;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
int temp;
scanf("%d", &temp);
sum += temp;
}
printf("和为%d", sum);
return 0;
}
```
六、scanf与输入缓冲区
当我们在scanf函数中输入字符时,输入缓冲区可能会出现问题。scanf会从输入流中读取数据,但是它不会清除输入缓冲区。
当scanf读取字符时,它会将字符存储在目标变量中,并且通常也会将字符留在缓冲区中。除非清除缓冲区中剩余的内容,否则其它输入函数可能无法正常工作。
例如,在以下代码中,由于scanf也会从输入流中读取并将回车键读取到缓冲区中,所以第二个scanf函数直接返回:
```c
char c;
scanf("%c", &c); //读取字符
scanf("%c", &c); //读取回车
scanf("%c", &c); //这一行直接返回
```
虽然上述代码可行,但是在实际编程中,尽量避免使用这种方法。以下代码可以避免scanf和缓冲区之间的问题:
```c
char c;
scanf("%c", &c); //读取字符
while (getchar() != '\n') //清除缓冲区
;
scanf("%c", &c); //读取回车
scanf("%c", &c); //读取另一个字符
```
在上述程序中,我们使用while循环清除缓冲区中的剩余内容。这避免了scanf读取缓冲区中的内容导致后续的输入出现错误问题。
七、scanf输入事件处理
在某些情况下,我们可能需要实现相应的输入事件处理。为了在读取键盘输入时处理事件,我们可以使用标准库中的getch()函数。
例如,在以下代码中,我们等待用户键入回车键并清除缓冲区中的其它输入:
```c
#include
#include
int main()
{
int num;
printf("输入一个整数:\n");
while (1){
if (kbhit()){ //如果有输入
scanf("%d", &num);
break;
}
}
while (getchar() != '\n') //清除缓冲区
;
printf("输入的整数是 %d.", num);
return 0;
}
```
在上述程序中,我们使用了一个循环来等待用户输入。kbhit()函数返回非零值,表明输入已经就绪。一旦接收到输入,它便用scanf读取输入,并清除缓冲区中的其余输入字符。最后,该程序会输出所收到的数字。
八、缓冲区切换
我们知道,scanf函数默认从标准输入设备(通常是键盘)读取输入值,并在缓冲区中存储输入值,才能够通过回车键来提交输入的内容。
scanf函数在获取输入值后,将在输入缓冲区中留下回车键,以便我们使用gets等函数之后直接读取用户输入的值。然而,如果我们使用快速切换输入设备时,会出现一些奇怪的行为。
例如,在下面的程序中,我们使用scanf从标准输入设备中读取输入并输出到标准输出设备中。
```c
#include
#include
int main()
{
char buffer[100];
printf("输入0~100之间的数字:\n");
scanf("%d", &num);
snprintf(buffer, 100, "输入的数字是 %d\n", num);
printf("%s", buffer);
return 0;
}
```
如果您现在使用上述程序而不等待用户输入,而是迅速使用文件重定位将其输入重定位到文件中,则会发现程序无法正确工作。这是因为scanf函数从终端设备中将输入获取到并存储到缓冲区中,导致输入文件上第一次scanf时发生的数据无法被读取。另外,对于同样的原因,如果我们先使用fgets函数从输入文件中读取一行,然后使用scanf函数读取其它来自终端设备的输入,前面读取的输入会被留在缓冲区中,导致scanf读取错误。
解决这个问题的方法是切换输入缓冲区。而在C语言中,有两个函数可以实现缓冲区的清空,分别是fflush和rewind函数。
scanf函数在使用fflush或者rewind函数之后,对输入/输出缓冲区都会进行重置,以便在缓冲区中存储的历史输入值不会对程序产生影响。
至此,本篇文章通过介绍scanf函数及其格式字符串、返回值、输入事件等方面的特点和注意事项,总结了如何正确使用scanf函数实现输入输出的一些技巧。希望读者通过本篇文章的学习和理解,能够在程序开发中合理运用scanf函数,避免常见的scanf函数错误,更加有效地实现输入输出。