C语言教程笔记
十一. scanf函数
printf函数将二进制表示的整数、浮点数、字符、字符串等按照转换规范转换成字符,并打印在控制台上。与之相反,scanf函数将键盘输入的字符串根据转换规范,转换成二进制表示的整数、浮点数、字符或字符串等。
#include <stdio.h>
int main()
{
char c;
short s;
int n;
long l;
float f;
double df;
scanf("%hhd %hd %d %ld %f %lf", &c, &s, &n, &l, &f, &df);
printf("%d %d %d %d %f %f\n", c, s, n, l, f, df);
return 0;
}
注意 scanf_s
目前不讨论安全函数,可以右击解决方案的Project1,打开属性;进行操作。(5. 下面会讲到哦)
运行程序后,在键盘输入:“1 2 3 4 5.6 7.8”。scanf函数,将字符串"1 2 3 4 5.6 7.8",根据转换规范, 分别转换为各类二进制数据,并存储到变量中。
printf函数将这些变量,按照转换规范,再转换为字符串,输出到控制台。
1. scanf函数的使用公式
- scanf是一个变参函数。
- scanf的第一个参数是字符串。
- scanf的第一个参数内容为匹配字符以及转换规范。
- scanf的后续参数,是转换完成后,数据的存放位置。
- 转换规范的写法与数量,需要与后续的参数类型和数量对应。
1.1 scanf是一个变参函数
和printf一样,scanf也是一个变参函数。变参函数中参数的数量和类型不确定。
1.2 scanf的第一个参数是字符串
1.3 scanf的第一个参数为匹配字符串以及转换规范
请注意:输入时,需要按照第一个字符串的形式进行输入,否则无法得到正确结果。
例1: scanf第一个字符串为 “%hhd %hd %d %ld %f %lf” ,每个转换规范使用空格分割。那么输入时需要用空格进行分割,形如 1 2 3 4 5.6 7.8 。
例2: scanf第一个字符串为 “%hhd,%hd,%d,%ld,%f,%lf” ,每个转换规范使用 , 分割。那么输入时需要用逗号进行分割,形如 1,2,3,4,5.6,7.8 。
例3: scanf第一个字符串为 “%hhd+%hd-%dx%ld/%f~%lf” ,转换规范使用±x/~分割。那么需要像这样输入 1+2-3x4/5.6~7.8 。
总而言之,scanf会将输入的字符串与第一个参数进行匹配,从而找到需要转换的部分。 若字符串匹配失败,将无法得到正确的转换结果。
1.4 scanf的后续参数,是转换完成后,数据的存放位置
scanf将输入的字符串按照对应的转换规范进行转换,转换完成后的二进制,将依次存放到后续参数所输入的变量地址当中。暂时这里不讨论什么是变量的地址,请记住两个规则:
如果scanf将转换后的二进制存储到基本变量当中,请在变量名前加&。
如果scanf将字符串存储到字符数组中,字符数组名不用加&。
1.5 转换规范的写法与数量,需要与后续的参数类型和数量对应
scanf("%hhd %hd %d %ld %f %lf",&c,&s,&n,&l,&f,&df)
转换规范的写法与数量,需要与后续的参数类型和数量对应
参数数量 | 参数类型 |
---|---|
hhd | char |
hd | short |
d | int |
ld | long |
f | float |
lf | double |
2. scanf函数具体做了什么
2.1 将输入字符串与第一个参数进行匹配
首先,scanf函数读取到输入的字符串。接着,scanf会将这个输入字符串与第一个参数的字符串进行匹配,找到输入字符串中的子串与转换规范的一一对应关系。
子串 “1” 对应转换规范 “%hhd”
子串 “2” 对应转换规范 “%hd”
子串 “3” 对应转换规范 “%d”
子串 “4” 对应转换规范 “%ld”
子串 “5.6” 对应转换规范 “%f”
子串 “7.8” 对应转换规范 “%lf”
2.2 根据转换规范将字符转换为二进制
子串与转换规范匹配好之后就开始转换环节。scanf将根据子串对应的转换规范,使用不同的转换方式,将子串转换为二进制。
不同转换规范代表的转换方式如下表:
长度指示符 | 转换规范 | 转换为某种类型的二进制 |
---|---|---|
hh | d | char |
h | d | short |
无 | d | int |
l | d | long |
长度指示符 | 转换规范 | 转换为某种类型的二进制 |
---|---|---|
ll | d | long long |
hh | u | unsigned char |
h | u | unsigned short |
无 | u | unsigned int |
l | u | unsigned long |
ll | u | unsigned long long |
无 | f | float |
l | f | double |
无 | c | 字符对应的ASCII码 |
无 | s | 字符串中字符对应的ASCII码 |
根据上表,对照例子中的转换方式如下。
子串 “1” 对应转换规范 “%hhd” ,将转换为char类型的二进制表示,1字节。
子串 “2” 对应转换规范 “%hd” ,将转换为short类型的二进制表示,2字节。
子串 “3” 对应转换规范 “%d” ,将转换为int类型的二进制表示,4字节。
子串 “4” 对应转换规范 “%ld” ,将转换为long类型的二进制表示,4字节。
子串 “5.6” 对应转换规范 “%f” ,将转换为float类型的二进制表示,4字节。
子串 “7.8” 对应转换规范 “%lf” ,将转换为double类型的二进制表示,8字节。
图片来自 你好编程
2.3 转换后的二进制放入变量
得到转换后的二进制后,将这些二进制根据顺序,依次放入变量当中。
图片来自 你好编程
由于使用了对应类型的变量来接收转换结果,所以长度和类型均可以保证一致。
3. 几类的错误示范
3.1 长度正确但类型错误
#include <stdio.h>
int main()
{
long long ll;
scanf("%lf", &ll);
printf("%lld\n", ll);
printf("%f\n", ll);
return 0;
}
输入了字符串 “123.45” ,该字符串被转换规范 “%lf” 匹配。
接下来,字符串 “123.456” 将被转换为double类型的二进制表示,8个字节。
最后,这8个字节被送给了 long long 类型的变量 ll 。
现在变量ll是一个装有double类型二进制的整型了。
使用 %d 来打印ll肯定出现了错误的结果。
那使用 %f 来打印呢? %f 将取8个字节的二进制,并且按照double类型二进制规则进行转换。结果 就得到了正确的结果。
3.2 输入字符串数值大于转换类型取值
#include <stdio.h>
int main()
{
short s;
scanf("%hd", &s);
printf("%d\n", s);
return 0;
}
输入了字符串 “2147483467” ,该字符串被转换规范 “%hd” 匹配。
接下来,字符串 “2147483467” 将被转换为short类型的二进制表示,2个字节。
而short类型的取值范围为 -32767~32768 , 2147483467 无法用short装下。
所以,无法得出正确的结果。
3.3 变量放不下转换结果
#include <stdio.h>
int main()
{
short s;
scanf("%d", &s);
printf("%d\n", s);
return 0;
}
输入了字符串 “2147483467” ,该字符串被转换规范 “%d” 匹配。
接下来,字符串 “2147483467” 将被转换为int类型的二进制表示,4个字节。
最后,转换后的4个字节的数据被short类型的变量s接收,丢失了2个字节。
所以,无法得出正确的结果。
3.4 如何避免错误
#include <stdio.h>
int main()
{
int n;
scanf("%d", &n);
printf("%d\n",n);
return 0;
}
我们输入了字符串 “2147483467” ,该字符串被转换规范 “%d” 匹配。
接下来,字符串 “2147483467” 将被转换为int类型的二进制表示,4个字节。
最后,转换后的4个字节的数据被int类型的变量n接收。
正确结果。
使用scanf的时候请注意,输入字符串的数值与转换规范和接收转换结果的变量类型必须匹配才能得到 正确结果。
4.字符和字符串
4.1 输入字符
#include <stdio.h>
int main()
{
char c;
scanf("%c", &c);
printf("%d %c\n", c, c);
return 0;
}
输入了字符串 “A” ,该字符串被转换规范 “%c” 匹配。
接下来,字符串 “A” 将被转换为char类型的二进制表示(其十进制为65),1个字节。
最后,转换后的1个字节的数据被char类型的变量c接收。
当用 %d 打印c时,输出了数值 65 。而用 %c 打印时,输出了字符 A 。
#include <stdio.h>
int main()
{
char c;
scanf("%hhd", &c);
printf("%d %c\n", c, c);
return 0;
}
如果要给char类型输入数值,请使用转换规范 %hhd 。若还使用转换规范 &c 。会将第一个字符的ASCII码装入变量。
4.2 输入字符串
#include <stdio.h>
int main()
{
char str[10];
scanf("%s", str);
printf("%s", str);
return 0;
}
c语言中没有字符串变量,字符串被存储在字符数组当中。 由于这里是将输入的字符串存储到字符数组中,后面的参数str不加&。 目前还没有讨论过数组,暂时不继续展开这一部分内容。
5. VS无法使用scanf函数
如果你使用的是较新版本的Visual Studio,scanf这类函数会被认为是不安全的,编译器会抛出C4996错 误,并建议你使用别的安全函数替代。
但是,目前我们并不想引入并讨论这一类安全函数。请将 _CRT_SECURE_NO_WARNINGS 加入预定义字符串, 屏蔽不安全函数错误。
如图所示:
6. scanf与printf的不同
- printf的后续参数不要加&,而scanf由于需要一个地址,所以对于基本变量需要加&,数组则不需要。
- printf的参数由于比int小的变量会升级为int,float会升级为double。所以,转换规范 d 可以用于char,short,int。转换规范f可以用于float和double。但是scanf是直接把转换结果送到接收变量中,必须严格使用转换规范。