------- android培训、java培训、IOS培训、期待与您交流! ----------
这是刚学习C语言的时候遇到的最早的一个问题,是以前从来没有接触过的一个全新知识点,在这里作为博客日志记下
来,方便以后翻阅.
绪论
我们知道,在c语言编程中,printf函数是其标准函数库stdio.h中的标准输出函数,scanf函数是其标准数据库stdio.h中的标
准输入函数
printf函数的基本语法是: printf(“格式化字符串”[,参数,参数…]);
其功能就是打印格式化字符串
scanf函数的基本语法是:scanf(“格式化字符串”,变量的地址);
其功能是允许用户输入数据,并将数据赋值给变量
scanf函数和printf函数在c语言中使用频度极其之高,语法调用极其简单,即使是没有任何编程基础的人在敲了两遍之后
也能熟悉并运用scanf和printf函数实现简单地获得用户的输入并将其打印到屏幕上的功能.
那么scanf函数和printf函数具体是如何实现数据读取与数据输出的呢,一般来说作为软件端的编程者并不太需要了解硬
件式的实现机制,但是往往不了解一些具体实现机制,我们很容易出现错误.
下面就以我前段时间的一道练习题为例来分析scanf函数和printf函数的具体数据处理方式,以及我们为什么要分析弄懂
这个:
#include
int main(int argc,constchar * argv[]) {
// 有些时候输入一些东西
// 简单计算器
// 1输入数字
// 2输入运算符
// 3输入数字
int num1, num2; // 两个整数变量,用于接收数字
char oper, temp; // 字符变量,用于接收运算符
printf("请输入数字:");
scanf("%d", &num1);
// 循环中和掉所有的回车
scanf("%c", &temp);
printf("请输入运算符:");
scanf("%c", &oper);
printf("请输入数字:");
scanf("%d", &num2);
scanf("%c", &temp);
// ...
printf("num1 = |%d|\noper =|%c|\nnum2 = |%d|\n", num1, oper, num2);
return 0;
}
如上面的代码所示,我们需要实现一个简单计算器功能,由用户依次输入第一个操作数,运算符,第二个操作数,我们需要用
代码实现其计算功能并输出结果
我们的第一想法是:
在主函数main中,
首先申明需要接受的三个变量:
int num1, num2;
char oper;
然后利用printf,scanf函数交替的方式让提醒用户输入我们需要的三个变量:
printf("请输入第一个数字:");
scanf("%d", &num1);
printf("请输入运算符:");
scanf("%c", &oper);
printf("请输入数字:");
scanf("%d", &num2);
然后利用三个接受到得变量在代码内实现运算并由printf函数进行结果输出
然而在编译运行后进行输出的时候,我们遇到了问题,当我们在编译结果面板输入连续的数据时,我们习惯用空格或者回
车来分割我们输入的数据,当这里输入”1+1”时,我们得到的我们程序代码想要获得的结果,然而当我们尝试输入”1回车
+回车1回车”时,当输入+号并敲击回车时,代码就已经跳出等待输入直接结束了,程序本身并没有报错,那么这是为什么
呢?
这也就是为什么我们需要在这里讨论关于printf和scanf这两个看似简单的基本函数库函数的数据读取与输入问题
我们先来了解一下printf函数的数据打印过程:
如图所示,printf函数本身并不具有调用硬件操作硬件的功能,具体的硬件操作与软件编码的对接,是由操作系统来实现的,
操作系统有一个软件应用接口,对于printf函数,有一个专门的缓冲区,每次只有当缓冲区满地时候才会将结果显示到屏幕
中,如果手动的提供\n表示自动结束缓冲区,就可以将结果打印出来.
而对于scanf函数的数据读取,也要涉及到这个数据缓冲区,scanf函数的数据读取过程是这样的:
scanf(.....) {
从缓存中获取数据
如果没有数据
调用阻塞的函数,等到用户输入,在从缓存中取数据
拿到数据后将数据与格式化字符串比较
获得的字符 =将匹配到的字符从缓存中取出
如果格式字符串是%d
那么就调用转换的函数,将字符串转换成数字
赋值给地址指向的变量
否则,如果格式字符串%c
那么直接将数据赋值给地址表示的变量
否则, ... %f
... %s
...
}
弄清了上面缓冲区的概念以及printf函数与scanf函数对于缓冲区数据的处理方式,我们就能够分析上面例题出错的原因,
并对上面例题的逻辑语法结构进行修正和改善了
在上面的例题中,当我们按第一种敲入”1+1回车”,输入完敲击回车时,printf函数在接收到回车符的时候,会判定用户输入
结束,将”1+1回车”这4个数据传入缓冲区,我们的scanf函数是可以对缓冲区实现实时读取的,因此在缓冲区出现数据的那
一刻,scanf函数开始数据读取与匹配,先读取1,与scanf (“%d”)进行匹配,+号不是int型数据,第一个scanf函数只读取1;1被
取走,第二个scanf函数从+号开始读取,%c与+号匹配,与1不匹配,取走+号,第三个scanf函数取走1,正是我们想要的数据
获取方式.
然而第二种输入方式则出现了问题.让我们按第二种敲入”1回车+回车1回车”时,第一个scanf函数获取1,然而到第二个
scanf函数读取%c型数据时,因为回车本身为’\n’,也是一个字符,因此第二个scanf函数读取的是’\n’而不是+号,第三个
scanf函数需要读取的是int型数据,而这时剩下的数据是”+回车1回车”,+号不是int型数据,匹配失败,第三个scanf退出,这
时程序的读取就已经结束了,但是这时屏幕并没有输出任何结果,直到敲下+号后的回车,这时函数才判断输入结束,输出
结果
经过上面的综合分析,我们就知道了到底是哪儿出的错,以及为什么出错,那么怎么修改呢,第一种输入并不符合我们的输
入习惯,我们想要按第二种方式输入,那么代码该怎样修改?
在这里,出错的原因在于每次输入后的回车符,我们引入一个新的字符型变量char temp来截获掉这个回车符就行了.在每
一次scanf获取数据后,加上一个scanf(“%c”,temp);这样就把’\n’截获并过滤掉了,从而实现了第二种方式输入的数据获取
scanf函数另外有一点需要注意的是scanf在数据输入的时候会自动忽略空白符,因此scanf(“%c\n”,c);这种读取方式是永
远不会结束的.
经过以上对printf和scanf函数的缓冲区数据读取打印分析,我们对这两个函数的具体实现流程有了更深的了解,并且随之
发散的思想是,在今后的学习过程中对函数的实现机制的具体实现的了解,也是很重要的.