导读:
为啥我会想到介绍这个?因为c语言中我们用到的最频繁的输入输出方式就是scanf()与printf()。
scanf(): 从标准输入设备(键盘)读取数据,并将值存放在变量中。 printf(): 将指定的文字/字符串输出到标准输出设备(屏幕)。注意宽度输出和精度输出控制。 C语言借助了相应的缓冲区来进行输入与输出。
如上简图
c的输入流:
int scanf(const char * restrict format,...);
函数 scanf() 是从标准输入流stdio (标准输入设备,一般指向键盘)中读内容的通用子程序,可以说明的格式读入多个字符,并保存在对应地址的变量中。 函数的第一个参数是格式字符串,它指定了输入的格式,并按照格式说明符解析输入对应位置的信息并存储于可变参数列表中对应的指针所指位置。每一个指针要求非空,并且与字符串中的格式符一一顺次对应。
流是什么?
“流”即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续且具有方向性的数据( 其单位可以是bit,byte,packet )的抽象描述。
“流”:是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”。
他的特性是:有序连续,具有方向性。
函数scanf()的返回值:
scanf("%d %d",&a,&b);
该函数的返回值为int型,返回成功读入的数据项数,读入数据时遇到了"文件结束"则返回EOF。
如果a和b都被成功读入,那么scanf的返回值就是2;
如果只有a被成功读入,返回值为1;
如果a和b都未被成功读入,返回值为0;
如果遇到错误或遇到end of file ,返回值为EOF。 (end of file 为Ctrl+z或者Ctrl+d)
注意
在输入字符数据(%c)时,若格式控制串中无非格式字符,则认为所有输入的字符均为有效字符。
例如:
scanf("%c%c%c",&a,&b,&c);
//输入为:d e f
则把’d’赋予a, ’ ‘(空格)赋予b,’e’赋予c。 只有当输入为:def(字符间无空格) 时,才能把’d’赋于a,’e’赋予b,’f’赋予c。 如果在格式控制中加入空格作为间隔,
如
scanf("%c %c %c",&a,&b,&c);
则输入时各数据之间可加空格。
问题
如何让scanf()函数正确接受有空格的字符串?如:I love you!
#include <stdio.h>
int main(void)
{
char str[80];
scanf("%s",str);
printf("%s",str);
return 0;
}
输入:
I love you!
输出:
I
上述程序并不能达到预期目的。因为scanf()扫描到”I”后面的空格就认为对str的扫描结束(空格没有被扫描),并忽略后面的” love you!”。值得注意的是,我们改动一下上面的程序来验证一下:
#include<stdio.h>
#include<windows.h>
int main(void)
{
char str[80],str1[80],str2[80];
scanf("%s",str);/*此处输入:I love you!*/
printf("%s\n",str);
scanf("%s",str1);/*这两句无需你再输入,是对stdin流再扫描*/
scanf("%s",str2);/*这两句无需你再输入,是对stdin流再扫描*/
printf("%s\n",str1);
printf("%s\n",str2);
return 0;
}
输出:
I
love
you!
所以结论是:残留的信息 love you!是存在于stdin流中,而不是在键盘缓冲区中。那么scanf()函数能不能完成这个任务?回答是:能!别忘了scanf()函数还有一个 %[] 格式控制符(想了解的更详细请自行搜索正则匹配)
#include<stdio.h>
int main(void)
{
char str[50];
scanf("%[^\n]",str);/*scanf("%s",string);不能接收空格符*/
printf("%s\n",str);
return 0;
对输入输出缓冲区的理解
1.为什么要引入缓冲区
例如,我们从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,故应用缓冲区可大大提高计算机的运行速度。
又比如,我们使用打印机打印文档,由于打印机的打印速度相对较慢,我们先把文档输出到打印机相应的缓冲区,打印机再自行逐步打印,这时我们的CPU可以处理别的事情。
现在您基本明白了吧,缓冲区就是一块内存区, 它用在输入输出设备和CPU之间,用来缓存数据 。它使得低速的输入输出设备和高速的CPU能够协调工作 ,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。
2.缓冲区的类型
-
全缓冲
在这种情况下,当 填满 标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
-
行缓冲
在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是标准输入(stdin) 和 标准输出(stdout) 。
-
不带缓冲
也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
3.缓冲区的大小
如果我们没有自己设置缓冲区的话,系统会默认为标准输入输出设置一个缓冲区,这个缓冲区的大小通常是 512个字节 的大小。
缓冲区大小由 stdio.h 头文件中的宏 BUFSIZ 定义,如果希望查看它的大小,包含头文件,直接输出它的值即可:printf("%d", BUFSIZ);
缓冲区的大小是可以改变的,也可以将文件关联到自定义的缓冲区,这里不做赘述。
结合缓冲区重新来看看getchar()函数
当程序调用getchar()函数时,程序就等着用户按键,并等用户按下回车键返回。期间按下的字符存放在缓冲区,第一个字符作为函数返回值。继续调用getchar()函数,将不再等用户按键,而是返回您刚才输入的第2个字符;继续调用,返回第3个字符,直到缓冲区中的字符读完后,才等待用户按键。 getchar()函数的执行就是采用了行缓冲。第一次调用getchar()函数,会让程序使用者(用户)输入一行字符并直至按下回车键 函数才返回。此时用户输入的字符和回车符都存放在行缓冲区。再次调用getchar()函数,会逐步输出行缓冲区的内容。