一、scanf和printf介绍
1.1 printf
1.1.1
printf()的作用是讲参数文本输出到屏幕。它的名字里f代表format(格式化),表示可以定制输出文本的格式。
这里就来到了大家熟悉的“Hello World”。
printf()不会在行尾自动添加换行符,运行结束后,光标就停留在输出结束的地方,不会自动换行。途中所示代码是添加了换行符\n之后的效果,从图中可以看出,换行符不光能写在要输出的文本内容末,也可以插入文本内部。
printf()是标准库的头文件stdio.h定义的。使用这个函数之前,必须在源码文件头部引入这个头文件。
这里我们不包含头文件就会出现这样的错误。点击否,点击下方错误列表,错误内容看的更直观。
这里有人就要问了,主播主播,这个getchar()在这里是干什么用的呢,为什么要写它呢?其实getchar是从键盘上读取一个字符的意思,它会读取用户输入的下一个字符,然后返回该字符的 ASCII 码值。
这里我用int承接一下它的返回值(因为getchar的返回值是int类型)。
这里我从键盘输入一个w,它就返回了一个w。
既然有了输入一个字符,那便有输出一个字符,putchar()的作用便和getchar()有着相反的作用,它的作用和printf一样道理。
当然,既然单独提出来了,它肯定有着和printf和scanf的不同之处,getchar()只能读取字符,putchar()只能输出打印字符,在前面“Hello World”图片之所以使用getchar()是为了更直观的看光标闪烁的位置,只要不输入字符,程序就不会执行下一步,就能直观的看到光标的位置啦。
这里再做一个小补充,虽然输入的是数字2,但getchar读取到的是一个字符2!
1.1.2
printf()可以在输出文本中指定占位符。
所谓“占位符”,就是这个位置可以用其他值代入。
上面的示例中,there are %d apples\n是输出文本,里面的%d就是占位符,表示这个位置要用其他值来替换。占位符的第一个字符一律为百分号%,第二个字符表示占位符的类型,%d表示这里代入的值必须是一个整数。
printf()的第二个参数就是替换占位符的值,上面的例子是整数3替换%d。执行后的输出结果就是there are 3 apples。
常用的占位符除了%d,还有%s表示代入的是字符串。
下图示例中(左边数字代表行号),%s表示代入得是一个字符串,所以printf()的第二个参数就必须是字符串,这个例子是zhangsan。执行后的输出就是zhangsan will come tonight。
下图中,输出文本%s says it is %d o’clock\n有两个占位符,第一个是字符串占位符%s,第二个是整数占位符%d,分别对应printf()的第二个参数(zhangsan)和第三个参数(10)。注意:这里不是有两个参数,是有三个,逗号前面一串内容是一个,第二个是zhangsan,第三个是10,不要搞错了。执行后的输出就是zhangsan says it is 10 o’clock。
注:printf()参数与占位符是一一对应关系,如果有n个占位符,printf()的参数就应该有n+1个,如果参数个数少于对应的占位符,printf()可能会输出内存中的任意值。这是什么意思呢,我们用下面这个图解释一下。
编译器也给出了错误原因。
当然,printf()还有很多玩法,这也需要在平时的练习中不断开发,如下图:
1.1.3 占位符列举
printf()的占位符有很多种类,与C语言的数据类型相对应。下面按照字母顺序,列出常用的占位符,方便查找。常用的加粗了。
- %a:十六进制浮点数,字母输出为小写。
- %A:十六进制浮点数,字母输出为大写。
- %c:字符。//char
- %d:十进制整数。//int
- %e:使用科学计数法的浮点数,指数部分的e为小写。
- %E:使用科学计数法的浮点数,指数部分的E为大写。
- %i:整数,基本等同于%d.
- %f:小数(包含float类型和double类型)。//float-%f double-%lf(其实这里无论float和double都可以用%f,但是为了便于区分两者的区别,还是建议分开写。
- %g:6个有效数字的浮点数。整数部分一旦超过6位,就会自动转为科学计数法,指数部分的e为小写。
- %G:等同于%g,唯一的区别是指数部分的E为大写。
- %hd:十进制short int 类型。
- %ho:八进制short int 类型。
- %hu:unsigned short int 类型。
- %ld:十进制long int 类型。
- %lo:八进制long int 类型。
- %lx:十六进制long int 类型。
- %lu:unsigned long int 类型。
- %lld:十进制long long int类型。
- %llo:八进制long long int类型。
- %llx:十六进制long long int类型。
- %llu:unsigned long long int类型。
- %Le:科学计数法表示的long double类型浮点数。
- %Lf:long double类型浮点数。
- %n:已输出的字符串数量。该占位符本身不输出,只将值存储在指定变量之中。
- %o:八进制整数。
- %p:指针(用来打印地址)。
- %s:字符串。
- %u:无符号整数(unsigned int)。
- %x:十六进制整数。
- %zd:size_t类型。
- %%:输出一个百分号。
这里补充一下,虽然在实际的正整数打印中,%u和%d的打印出来的效果看起来没有区别。
但是!这是不对的,而且,如果你用负数,初始化了一个无符号整型,它会当成一个非常大的整数进行解析。如下图:
具体原因小编能力有限也不知道哈哈,如果后面理解了会出新的文章来作解释。
但是呢,如果用%d打印unsigned int定义的无符号整型,就可以正常打印。用%u打印int定义的有符号整型就可以正常打印,具体原因小编也不是很懂,还是规规矩矩一个萝卜一个坑吧。
1.1.4 输出格式
printf()可以定制占位符的输出格式
1.1.4.1 限定宽度
printf()允许限定占位符的最小宽度。
上面示例中,%5d 表示这个占位符的宽度至少为5位。如果说不满5位,对应的值的前面会添加空格。输出的值默认是右对齐,即输出内容前面会有空格;如果希望改为左对齐,在输出内容后面添加空格,可以在占位符的%d的后面插入一个-号。
上面示例中,输出内容123的后面添加了空格。
如果我们用了%5d,却想输出6位呢?
结果就是,想输出6位那就输出6位,不够了再补空格。
对于小数,这个限定符会限制所有数字的最小显示宽度。
上面示例中,%12f表示输出的浮点数最少要占据12位。由于小数的默认显示精度是小数点后6位,所以123.45输出结果的头部会添加两个空格。
1.1.4.2 总是显示正负号
默认情况下,printf()不对正数显示+号,只对负数显示-号。如果想让整数也输出+号,可以在占位符的%后面添加一个+。
上面示例中,%+d可以确保输出的数值,总是带有正负号。
1.1.4.3 限定小数位数
输出小数时,有时希望限定小数的位数,举例来说,希望小数点后面只保留两位,占位符可以写成%.2f。
那么能不能总宽度限制和小数点后面的位数限制结合使用呢?当然是可以的。
在这里再介绍一种写法,最小宽度和小数位数这两个限定值,都可以用 * 代替,通过printf()的参数传入。
上面示例中,%星号.星号通过printf()的两个参数10和2传入。这样的写法的优点就是更加灵活。
1.1.4.4 输出部分字符串
%s占位符用来输出字符串,默认是全部输出。如果只想输出开头的部分,可以用%.[m]s指定输出的长度,其中[m]代表一个数字,表示要输出的长度。
1.2 scanf
当我们有了变量,我们需要给变量输入值就可以使用scanf函数,如果需要将变量的值输出在屏幕上的时候可以使用printf函数,这里就可以解释一下我之前所有博客内容图片第一行代码:#define _CRT_SECURE_NO_WARNINGS的作用,下面看一个例子:
然而当我们运行程序的时候,却发现报出了错误,这句报错的意思是scanf这个函数可能不安全,可以考虑使用scanf_s函数替换scanf,如果想让这个描述失效的话,请使用#define _CRT_SECURE_NO_WARNINGS,其实这种错误现象是VS中独有的,只要将#define _CRT_SECURE_NO_WARNINGS放在使用了scanf函数的源文件的第一行,就可以正常使用scanf函数了,但每次写代码都输是不是太麻烦了,大家可以在csdn里找文件进行解决,我这里就不作演示了。
这里是VS2022的报错,VS2022认为scanf函数不安全,确实有不安全的地方,为什么不适用scanf_s呢?
因为scanf_s函数是vs202自己提供的,不是C语言标准提供的
如果你在代码中使用了scanf_s,那么这个代码只有vs2022能编译。
其他的编译器不认识scanf_s的,那也就不能编译代码,这个代码就失去跨平台性。
运行截图:
1.2.1
scanf()函数用于读取用户的键盘输入。程序运行到这个语句时,会停下来,等待用户从键盘输入。
用户输入数据、按下回车键后,scanf()就会处理用户的输入,将其存入变量。
它的原型定义在头文件stdio.h。
scanf()的语法跟printf()类似。
它的第一个参数是一个格式字符串,里面会放置占位符(与printf()的占位符基本一致),告诉编译器如何解读用户的输入,需要提取的数据是什么类型。
这是因为C语言的数据都是有类型的,scanf()必须提前知道用户输入的数据类型,才能处理数据。
它的其余参数就是存放用户输入的变量,格式字符串里面有多少个占位符,就有多少个变量。
上面示例中,scanf()的第一个参数%d,表示用户输入的应该是一个整数。%d就是一个占位符,%是占位符的标志,d表示整数。第二个参数&score表示,将用户从键盘输入的整数存入变量i。
注意:变量前面必须加上&运算符(指针变量除外),因为scanf()传递的不是值,而是地址。即将变量score的地址指向用户输入的值。
如果这里的变量是指针变量(比如字符串变量),就不用加&运算符。
上面示例中,格式字符串%d%d%f%f,表示用户输入的前两个是整数,后两个是浮点数,比如:1,-20,3.4,-4.0e3。这四个值依次放入i、j、x、y四个变量。
scanf()处理数据占位符时,会自动过滤空白字符,包括空格、制表符、换行符等。
所以,用户输入的数据之间,有一个或多个空格不影响scanf()解读数据。另外,用户使用回车键,将输入分成几行,也不影响解读。
上面示例中,用户分成四行输入,得到的结果与一行输入是完全一样的。每次按下回车键后,scanf()就会开始解读,如果第一行匹配第一个占位符,那么下次按下回车键时,就会从第二个占位符开始解读。
scanf()处理用户输入的原理是,用户的输入先放入缓存,等到按下回车键后,按照占位符对缓存进行解读。
解读用户输入时,会从上一次解读遗留的第一个字符开始,直到读完缓存,或者遇到第一个不符合条件的字符为止。这里我们依旧用一串代码作解释:
上面示例中,scanf()读取用户输入时,%d占位符会忽略起首的空格,从-处开始获取数据,读取到-13停下来,因为后面的.不属于整数的有效字符。这就是说,占位符%d会读到-13.
第二次调用scanf()时,就会从上一次停止解读的地方,继续往下读取。这一次读取的首字符是.,由于对应的占位符是%f,会读取到.45e12,这是采用科学计数法的浮点数格式。后面的#不属于浮点数的有效字符,所以会停在这里。然而,为什么打印出来的数据不是0.4510的12次方呢?
这是因为浮点数在内存中有可能无法精确保存,导致有误差,而且这里是0.4510的12次方,进一步扩大了误差,就会出现0.449999994880*10^12这样的值,实际上是无限接近我们要得出的值的。
我们也可以这样,打印出来的效果是一样的
1.2.2 scanf 的返回值(没想到吧,scanf居然还有返回值)
scanf()的返回值是一个整数(int),表示成功读取的变量个数。
如果没有读取任何项,或者匹配失败,则返回0。
如果在成功读取任何数据之前,发生了读取错误或者遇到读取到文件结尾,则返回常量EOF(-1)。 //EOF是C语言中定义的一个值,这里我们转到定义解释。
EOF-end of file 文件结束标志。
这里我们可以看到EOF的值是-1。
我们再用一串代码和几张图片形象的反应scanf()的返回值
这里三个数据都成功读取到。
接下来就是读取错误的啦
这里三个Ctrl+z,就会让scanf读取发生错误,在vs上需要按3次,在DevC++只需要一次,我个人认为,这是编译器上的BUG。
1.2.3 占位符
这里简单说一下特殊的
在输出的时候
printf:
float----------------------------%f
double-------------------------%f %lf
long double------------------%Lf
scanf:
float----------------------------%f
double------------------------%lf
long double------------------%Lf
可以看到,在输出的时候,double无论用%f还是%lf都可以,但是在输入的时候,只能用%lf
%[ ]:在方括号中指定一组匹配的字符(比如%[0-9]),遇到不在集合之中的字符,匹配将会停止。
占位符中,除了%c以外,都会自动忽略起首的空白字符。%c不忽略空白字符,总是返回当前第一个字符,无论该字符是否为空格。这里我们用一张图解释:
这里的红色三角不用理会,这是小编刚刚上传了gitee,又修改了内容导致的,有兴趣的朋友自己可以下去查相关资料。
上图内容是不是就返回了第一个字符,没有跳过空白字符,但是,如果要强制跳过字符前的空白字符,可以写成scanf(“ %c",&ch),即%c前加一个空格,表示跳过零个或多个空白字符。
这样就可以正常打印了。
下面还要特别说一下占位符%s,它其实不能简单地等同于字符串,它的规则是,从当前第一个非空白字符开始读起,直到遇到空白字符(即空格、换行符、制表符等)为止。即如下图:
因为%s不会包含空白字符,所以无法用来读取多个单词,除非多个%s一起使用。这也意味着,scanf()不适合读取可能包含空格的字符串,比如书名和歌曲名。另外,scanf()遇到%s占位符,会在字符串变量末尾存储一个空字符\0。
同样,这样写也可以
scanf()将字符串读入字符数组时,不会检测字符串是否超过了数组长度。所以,储存字符串时,很可能会超过数组的边界,导致预想不到的结果。
就比如说这样,或者下图这样,预想不到嘛
这也是前面所说的scanf函数不安全的地方。
为了防止这种情况,使用%s占位符时,应该指定读入字符串的最长长度,即写成%[m]s,其中[m]是一个整数,表示读取字符串的最大长度,后面的字符将被丢弃。
这样就不会有数组溢出的风险啦!
就像小编的一位老师给小编说过,只要思想不滑坡,办法总比困难多!虽然scanf函数在VS里不安全,但总是有对应的解决办法的。
如果有人就是要问,小编小编,真的没有办法能在一个字符串内读取空格并打印出去吗,有的兄弟有的,这样的方法当然是有的,小编就在这里再介绍一个方法。
1.2.4 赋值忽略符
有时,用户的输入可能不符合预定的格式
这串代码正确的输入格式是这样,输入时候的空格只是为了区分3和4的读取
这是错误举例
这里scanf先输入一个整型是3,在输入一个整型遇到逗号,发生匹配失败了,就不往后读了。
所以输入数据的格式和代码中的格式串要保持一致才能得到正确的数据。
这里如果你真的很想要那个逗号,也可以这样
注意,用中文逗号就要输入中文逗号,否则格式也对不上。
为了避免这种情况,scanf()提供了一个赋值忽略符*。只要把*加在任何占位符的百分号后面,该占位符就不会返回值,解析后将被丢弃。展示如下:
上面示例中,%*c就是在占位符的百分号后面,加入了复制忽略符 *,表示这个占位符没有对应的变量,解读后不必返回。(不止b%c,%d,%f也一样)。
好啦,今天小编的技术总结就到这里啦,喜欢的靓仔靓女点赞收藏关注支持一下哈哈,满满干货,字数也是达到了1w1,希望能对大家有所帮助。