在C语言中,scanf()函数被包含于stdio.h的头文件中。而scanf()在程序中被用来读取用户键盘的输入,程序运行到这个语句时,会停下来等待用户从键盘输入,等到用户按下回车键时,scanf()会处理数据,将其存入变量,其语法和printf()相似。
一、scanf的基本用法
scanf()的用法和printf()相似,在输入数据时会放置占位符(和printf()的占位符一致),目的在于告诉编译器需要提取用户输入的数据类型,scanf()只有知晓输入数据类型后,才能处理数据,如图 1。在使用scanf()时,注意变量要取地址,因为scanf()传递的不是值,而是地址,但是如果传递的是指针变量就不需要加&。
图 1
如图 2所示,用户输入的是3.1415*,可以看到scanf()在第一次取值时被告知取整型所以取到3,而第二个scanf()被告知取浮点数,而 * 不属于浮点类型,所以取到*之前。
由此可以看出scanf处理用户输入的原理,用户的输入先放入缓存,等按下回车后,按照占位符对缓存进行解读。解读用户输入时,会从上一次解读遗留的第一个字符开始,直到读完缓存,或者遇到不符合条件的字符为止。当然由于scanf()可以输入两个占位符,所以图2这个例子也可以像图3一样。
图 2
图 3
与此同时, scanf()处理数值占位符时,会自动过滤空白字符,包括空格、制表符(Tab键)、换行符等。所以用户在输入数据时有空格或换行不影响scanf()解读数据,如图 4。
图 4
二、scanf的特殊用法
在scanf()函数常用的占位符中,我认为有几个需要注意的存在,它们分别为%c,%*c,
%[ ]。
1、%c和%*c
%c在printf()和scanf()中都是字符的占位符,%c不会忽略起首的空白字符,其他的大都数占位符会自动忽略空格,如图 5。
图 5
scanf()函数提供赋值忽略符,把*加到任何占位符的百分号后面,该占位符就不会返回值,解析后直接丢弃。在实际生活中,有时用户输入的数据不符合预定的格式。比如让用户输入日期,根据个人的习惯不同,每个被输入的数据可能都不一样。例如,2023 12 23、2023,12,23或者2023/12/23等等,这就可能会导致使用数据的时候造成数据的丢失,如图6。而使用赋值忽略符很好地解决了这一问题,如图 7。
图 6
图 7
2 %[ ]
在vs2019、vs2022等这两个IDE中使用scanf()函数,编译器会给我们发出警告,说这个函数不安全,建议让我们使用vs自创的scanf_s,scnaf()在存储数据的过程中只要没有遇到终止条件,scanf不管能不能放下,他会无条件地存储,这就会导致数组越界,非法访问其他内存,产生安全问题。
scanf_s()是c11标准中引入的新函数,scanf_s()中我们要指定最大的读取字符数,也就是我们听说过的“缓冲区边界”。而在scanf()函数中使设定读入字符的长度,也可以达到类似的效果,如图 8。写成%[m]s,其中m为一个整数,表示读取字符的最大长度,后面的字符将被丢弃。
图 8