一起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函数。欲知后面还有什么例子,且听下回分解。


0 0
原创粉丝点击