一起talk C栗子吧(第三十八回:C语言实例--你了解scanf吗)
来源:互联网 发布:java连接mysql的jar包 编辑:程序博客网 时间:2024/05/22 11:30
各位看官们,大家好,上一回中咱们说的是获取当前日期和时间的例子,这一回咱们说的例子是C语言中的库函数:scanf。闲话休提,言归正转。让我们一起talk C栗子吧!
看官们,说到C语言中的库函数:scanf,我想大家都认识它,而且知道它是用来从标准输入中(这里可以当作是终端)获取输入的内容,不过关于该函数的一些小细节,我估计大部分人都不是十分了解,接下来我就把这些小的细节说给大家听。
scanf函数有返回值
大家在平常使用scanf函数时,都是为了从终端中获取输入的值,比如:scanf(“%d”,&a)会从终端中获取一个int类型的值,并且把该值保存在变量a中。大家都忽略了它还有一个返回值,该返回值为int类型,它表示scanf从终端中读取值的数量,比如:scanf(“%d”,&a)的返回值为1.如果它返回了0,说明从终端读取数值失败。因此,我们在写程序的时候,最好判断一下scanf的返回值,通过返回值来确认是否从终端中成功地读取数值,这样可以提高程序的健壮性。例如:if(1!=scanf(“%d”,&a)) exit(0);
我们举一个实际的例子来说明:
//相信大家都能看明白,因此main等相关的内容就不写出来了printf("Please input a number,ex: 3 \n");if(1 != scanf("%d",&iVal)) // confirm the inputting option{ printf("Can't get the number \n"); return 1;}else printf("get the number %d normally \n",iVal);
编译并且运行程序后可以得到以下结果:
Please input a number,ex: 3 2 //手动输入数字2get the number 2 normally
大家从程序的运行结果中可以看到,程序运行到了else分支中,这说明scanf返回了数值1.并且把我们在终端中输入的数字2,存放到变量iVal中。
scanf函数获取输入内容的优点和缺点
scanf函数的第一个参数是用来控制格式的,它可以控制读取的数值类型,比如读取int类型的数值时需要使用%d.这个功能,我就不多说了,我相信大部分看官们都知道,甚至可能像本山大叔的广告语:地球人都知道。哈哈。
它的另外一个功能是控制输入数值的格式,比如scanf(“%d “,&a)注意:在d后面有一个空格符,该例子表示从终端中获取int类型的数值直到遇到非空白字符为止。这里的空白字符包括空格,TAB键,换行符。
我们举一个实际的例子来说明:
printf("Please input a number,ex: 3 \n");if(1 != scanf("%d ",&iVal)) // there is a space at the end of %d{ printf("Can't get the number \n"); return 1;}else printf("get the number %d normally \n",iVal);
编译并且运行程序后可以得到以下结果:
Please input a number,ex: 3 6 //手动输入数字6,这时程序没有停止,它还在等待获取非空白字符7 //再次输入非空白字符,也就是数字7,程序才结束get the number 6 normally
大家,我在注释里已经写清楚了,相信大家都能看明白其中的原理。大家可能会问,刚才输入的哪个数字7到哪里呢?其实它还在输入缓冲区中,如果在刚才的程序下面写以下程序:
scanf("%d",&iVal)printf("get the number %d normally \n",iVal);
它会得到以下结果:
get the number 7 normally //从输入缓冲区中获取数字,并且存放在变量iVal中
有看官提问:如果把上面的例子中的空格,移动到%d前面,也就是scanf(” %d”,&iVal),那么表示什么意思呢?它和不加空格的意思相同。也就是说:scanf(” %d”,&iVal)和scanf(“%d”,&iVal)得到的效果是一样的,我们通过实际的例子来说明:
printf("Please input a number,ex: 3 \n");if(1 != scanf(" %d",&iVal)) // there is a space before %d{ printf("Can't get the number \n"); return 1;}else printf("get the number %d normally \n",iVal);
编译并且运行程序后可以得到以下结果:
Please input a number,ex: 3 6 //手动输入数字6,这时程序停止,输出运行结果。它不会像上面的例子中等待获取非空白字符get the number 6 normally
其实,scanf函数本身就有过滤空白符的功能,它从终端中读取数字或者字符,直到遇到非空白的数字或者字符为止,因此我们没有必要在它的第一个参数中加入空格,除非有专门的需要。那种以为加入空格就能过滤空白符的做法是错误的。
我们举个例子说明:
printf("Please input 3 number,ex: 3 \n");if(3 != scanf("%d%d%d",&iVal,&a,&b)) // confirm the inputting option{ printf("Can't get the number \n"); return 1;}elseprintf("get the number %d %d %d normally \n",iVal,a,b);
编译并且运行程序后可以得到以下结果:
Please input 3 number,ex: 3 7 8 //手动输入数字7和8,在7前面和后面都有空格,scanf自动过滤它们,等待输入第三个数字9 //输入8后按下回车,然后输入第三个数字9。回车也是一种空白符,所以被scanf过滤掉了, 直到输入9它才结束get the number 7 8 9 normally
大家,我在注释里已经写清楚了,相信大家都能看明白其中的原理。
scanf函数自动过滤空白符的功能可以看作是它的一个优点,不过该功能也有一些副作用,就当作是它的缺点吧。比如在获取完int或者float等非字符类型的数值后,它会把这些数值后面的回车符放到输入缓冲区中,这时再使用scanf或者其它的输入函数(比如getchar)获取字符类型的数值时就会发现获取到是的回车符。我们举个例子来说明:
// get int value firstlyprintf("Please input a number,ex: 3 \n");if(1 != scanf("%d",&iVal)) // confirm the inputting option{ printf("Can't get the number \n"); return 1;}else printf("get the number %d normally \n",iVal);// get char value secondlyprintf("Please input a char ex: a \n");if(1 != scanf("%c",&ch)) // confirm the inputting option{ printf("Can't get the number \n"); return 1;}else printf("get the number %c<==>%d normally \n",ch,ch);
编译并且运行程序后可以得到以下结果:
Please input a number,ex: 3 4 //输入数字4,然后回车get the number 4 normally Please input a char ex: a get the number //我都没有输入字符,它就自动读取了4后面的回车符,并且显示回车<==>10 normally //回车符不太直观,所以我显示了它的的ASII码
大家,我在注释里已经写清楚了,相信大家都能看明白其中的原理。另外,大家可以查一下ASII码表,通过查表就能发现回车符的ASII码就是10。
scanf函数会造成缓冲区溢出
使用scanf获取字符串时会有缓冲区溢出的风险。比如scanf(“%s”,array)表示从终端中获取一个字符串,并且把获取到的字符串存放在数组array中。看官们有没有想过,如果用户输入的字符串长度大于数组array的容量时会有什么结果。我可以告诉你,这时候发生了严重的问题:缓冲区溢出。
有没有办法能够让用户输入的字符串长度小于数组array的容量呢?这个有点难度,因为用户想输入什么内容,我们没有办法知道,而且也不能阻止用户输入过长的字符串呀。怎么办?我们可以通过修改scanf的第一个参数来手动指定scanf读入串的长度,这样就可以确保scanf从终端中获取的字符串长度比数组的容量小,进而避免发生缓冲区溢出。
下面我们举一个实际的例子:
// be carefull for overfollowprintf("Please input a string ex: hello \n");if(scanf("%7s",array)!= 1) // confirm the inputting option, 1 means 1 string{ printf("there is a voerfollow \n"); return 1;}else printf("get the string %s \n",array);
编译并且运行程序后可以得到以下结果:
Please input a string ex: hello 123456789 //在终端中输入长度为9的字符串get the string 1234567
看官们,我们修改了scanf的第一个参数:%s==>%7s,这表示读取长度为7的字符串。从程序的运行结果看,scanf完全是按照我们的要求,只从终端中读取了字符串中的前7个字符。这样就可以避免发生缓冲区溢出,提高了程序的健壮性。
看官们,我们在正文中为了说明scanf函数的原理,代码写的很散,完整的代码放到了我的资源中,大家可以点击这里下载使用。
各位看官,关于“你了解scanf吗”的例子咱们就说到这里,希望在我们说完这个章回后,大家能够更加深刻地了解scanf函数。欲知后面还有什么例子,且听下回分解。
- 一起talk C栗子吧(第三十八回:C语言实例--你了解scanf吗)
- 一起talk C栗子吧(第二十八回:C语言实例--希尔排序)
- 一起talk C栗子吧(第四十八回:C语言实例--走迷宫二)
- 一起talk C栗子吧(第七十八回:C语言实例--创建进程)
- 一起talk C栗子吧(第一百一十八回:C语言实例--线程死锁二)
- 一起talk C栗子吧(第三回:C语言实例--求阶乘)
- 一起talk C栗子吧(第三十三回:C语言实例--巧用移位)
- 一起talk C栗子吧(第三十五回:C语言实例--测试程序运行时间)
- 一起talk C栗子吧(第三十六回:C语言实例--螺旋矩阵)
- 一起talk C栗子吧(第五十八回:C语言实例--获取程序当前的工作路径)
- 一起talk C栗子吧(第六十八回:C语言实例--DIY字符串长度函数的小疑问)
- 一起talk C栗子吧(第八十八回:C语言实例--使用管道进行进程间通信一)
- 一起talk C栗子吧(第九十八回:C语言实例--使用消息队列进行进程间通信二)
- 一起talk C栗子吧(第一百二十八回:C语言实例--查看当前进程信息)
- 一起talk C栗子吧(第三十四回:C语言实例--巧用溢出计算最值)
- 一起talk C栗子吧(第三十七回:C语言实例--获取当前日期和时间)
- 一起talk C栗子吧(第三十九回:C语言实例--创建一棵二叉树)
- 一起talk C栗子吧(第四回:C语言实例--斐波那契数列)
- Android Studio - no debuggable applications 的解决办法
- itoo项目实战之itoo中为什么要用EJB3.0分布式框架
- AFN的使用
- hdoj 1869 六度分离
- KVO
- 一起talk C栗子吧(第三十八回:C语言实例--你了解scanf吗)
- springmvc学习笔记(四)访问静态资源
- 从网上找到的加载刷新的方法
- Wing IDE 如何设置 python版本
- leetcode 150: Evaluate Reverse Polish Notation
- linux基础操作----任务调度指令
- 谓词与通知
- Js/Jquery获取iframe中的元素
- 黑马程序员--Java基础学习(面向对象)第五天