前言
scanf函数和printf函数是我们在C语言中使用最频繁的两个函数,它们用来格式化输入和输出。接下我将给大家介绍printf函数和scanf函数使用方法和注意技巧,我也是自学可能有些地方有错误,有些知识点可能解释错了,希望大神们指点和批评。
printf 函数
printf函数是C语言标准库中的输出函数,是设计用来显示格式串中的内容,并且可以在格式串指定的位置插入可能的值。在调用printf函数时必须提供格式串,格式串后面的参数是显示在格式串中的值:
printf(格式串,表达式1,表达式2,表达式2…)
显示的值可以是复杂的表达式可以是常量可以是变量。而且调用一次printf函数打印显示的值没有限制。
而格式串中包含普通字符和转换说明,其中以 % 开头的是转换说明符。转换说明是用来表示打印过程中待填充的值的占位符。跟在字符 % 后面的字母决定把数值从内部(二进制)形式转换成打印形式的方法。例如,转换说明 %d 表示printf把内部的整型二进制数值转换成打印时需要的十进制数值格式输出在相应的占位符的位置中。%f 把内部的整整型二进制数值转换成浮点数格式输出。
格式串中的普通字符完全如在字符串中的那样显示出来,而转说明则要用待显示的值来替换,例如:
#include<stdio.h>
int main(void){
int i,j;
float x,y;
i=10;
j=20;
x=43.2892f;
y=5527.0f;
printf("i= %d,j= %d,x= %f,y= %f\n",i,j,x,y);
return 0;
}
则输出结果是:
当然C语言编译器不会检测格式串当中的转换说明的数量和输出项的数量是否相匹配。接下来这个printf函数调用时转换说明的数量多于要显示的值的数量:
printf("%d %d\n",i); //转换说明的数量多余显示的变量个数
可以看到 Visual studio 2019 集成开发环境没有检测转换说明的数量和输出项的数量是否匹配。变量i正确显示了大小,接着显示了另一个无意义的数值。
此外,C语言编译器也不会检测转换说明是否适合要显示想的数据类型。一定要注意转换说明的用法和数量匹配,如下错误:
printf函数严格服从于格式串,所以会先显示float型数值,接着显示int型数值,可惜两个数值都是无意义的数值。
但发生了有趣的现象换成 Visual Code 编译运行居然报错了,大概意思是格式串中转换说明数量和参数数量不匹配。如下
:
转换说明
此外转换说明提供了大量对输出格式控制的方法。具体来说,我们可以用 %.1f 来显示小数点后带一位数字的float型数值。一般地,转换说明有两种格式 %m.pX 和 %-m.pX ,这里面的 m 和 p 都是整数常量,而 X 是字母。m 和 p 都是可选的。如果省略了 p ,则 m 和 p 之间的小数点也要去掉。在转换说明 %10.2f 中 m 是10,p 是2,而 X 是 f 。在转换说明 %10f 中 m 是10,p (连同小数点省略了),而在转换说明 %.2f 中 p 是2,m 则省去了。
最小字段宽度
-
m 指定了要显示的最少字符数量。如果要显示的数值所需的字符少于 m ,那么值在字段内是右对齐的(也就数值前面放置额外的空格),例如转换说明 %5d 将以 _ _ _ 24的形式显示数24,如下显示( 本文章将用符号 _ 表示空格):
-
但如果要显示的数值所需的字符多于 m ,那么字段宽度会自动扩展为所需的尺寸。不会丢失数据,以转换说明符 %4d 将以123456形式显示数123456 。例如:
-
在 m 前面放个负号会导致左对齐,转换说明 %-5d 将以123._ _ 形式显示123,例如( 本文章将用符号 _ 表示空格):
精度
上述转换说明格式 %m.pX 当中的 p 的含义很难描述,因为它依赖于转换说明符 X 的选择。X 表明在显示数值前面要对其进行哪种转换。对数值来说最常用转换说明符有以下几个。
-
d 表示十进制(基数为10)形式的整数。难么这个时候 p 指明了待显示的数字的最少个数(必要时在前加上额外的零)如果省略了 p ,则默认它的值为 1 。例如:
-
e 表示指数(科学计数法)形式的浮点数。p 指明了小数点后应该出现的数字的个数(默认值为6)。如果 p 为 0,则不显示小数点(即p为零小数点也没有)。
-
f 表示 “定点十进制” 形式的浮点数,没有指数。p 的含义与说明符 e 的作用一样。
-
g 表示指数型或定点十进制形式的浮点数,显示形式的选择根据数的大小决定。p 说明可以显示的有效数字(整数部分和小数部分加起来,即去掉小数点)的最大数量。与转换说明符 f 不同,g 的转换将不显示尾随的零。如果想要显示的数值没有小数后的数字,g 就不会显示小数点。
#include<stdio.h>
int main(void){
printf("%g\n", 3.14159263458796); // p 为零显示6为有效数字
printf("%.5g\n", 35356365453.14159263458796); // p 说明显示的是整数位有效数字的最大数量
printf("%.4f\n", 21.3); // .4f 表示显示小数点后有效数字的最大数量
printf("%.4g\n", 21.3); // .4g 显示小数点4位有效数字,而该显示的数字位不足规定位数时后面不加零
printf("%.10f\n", 3.1415928789879); //
printf("%.20g\n", 3.141592878987945465476785645);
return 0;
}
下面调用 printf 函数编写一个程序综合应用各种格式显示整数和浮点数的方法。
显示时,printf 函数格式串中的字符 “ | ” 只是用来帮助显示每个数所占用的空格数量。不同于 % 或者 \ ,字符 | 对 printf 函数而言没有任何特殊意义。
- %d 以十进制的形式显示变量 i 。
- %5d 以十进制的形式显示变量 i ,且至少占用5个字符的空间。因为变量 i 只占连个字符,所以添加了3个空格。
- %-5d 以十进制的形式显示变量 i ,且至少占用5个字符的空间。因为表示变量 i 的值不需要用到5个字符,所以在后续位置上添加空格(确切地说,变量 i 在长度为5的字段内是左对齐的)。
- %5.3d 以十进制的形式显示变量 i ,且至少占用5个字符的空间并至少有3位数字。因为变量 i 只有2个字符长度,所以要添加额外的零来保证有3位数字。现在只有3个字符长度,为了保证占有5个字符,还要添加2个空格(变量 i 是右对齐的)。
- %10.3f 以定点十进制形式显示变量 x 且总共十个字符,其中小数点后保留3位数字。因为变量 x 只需要7个字符(即小数点前3位,小数点后3位,再加上小数点本身1位),所以在变量 x 前面有3个空格。
- %10.3e 以指数形式显示变量 x ,且总共有10个字符,其中小数点后保留3位数字。因为变量 x 总共需要9个字符(包括指数),所以变量 x 前面有1个空格。
- %-10g 即可以以定点十进制形式变量 x ,也可以以指数形式显示变量 x ,且总共用10个字符。在这种情况下,printf 函数选择用定点十进制显示变量 x 。负号的出现强制进行左对齐,所以有4个空格跟在变量 x 的后面。
scanf函数
如同 printf 函数用特定的格式显示输出一样,scanf 函数也根据特定的格式读取输入。像 printf 函数的格式串一样,scanf 函数格式串包含普通字符和转换说明两部分。scanf 函数转换说明的用法和 printf 函数的转换说明用法本质上是一样。
在一般情况下,scanf 函数的格式串只包含转换说明,如下例所示:
像 printf 函数一样,scanf 函数也不会检测转换说明和要输入的变量的数量是否一致,并且也不会检查每个转换说明是否适合相对应的变量。与用 printf’ 函数一样,编译器不会检查可能存在的匹配不当。值得一提的是通常把符号 & 放在 scanf 函数调用中的每个变量的前面。
scanf 函数的工作方法
scanf 函数本质上是一种“模式匹配”函数,试图把输入的字符组与转换说明相匹配。
scanf 函数从左边开始处理字符串中的信息对于格式串中的每一个转换说明,scanf 函数从输入的数据中定位适当类型的项,并在必要时跳过空格,然后 scanf 函数读入数据项,并且在遇到不可能属于此项的字符时停止。如果读入数据项成功,那么 scanf 函数会继续处理格式串的剩余部分:如果某一项不能成功读入,那么 scanf 函数将不再查看格式串的剩余部分(或者余下的输入数据)而立即返回。
在寻找数的起始位置时,scanf 函数忽略空白字符(包括空格符,水平和垂直制表符,换页符和换行符)。考虑下面的 scanf 函数的调用:
虽然我们这样输入了变量的值,但 scanf 函数会把它们看成一个连续的字符流:
· · 1 # -20 · · · .3 # · · · -4.0e3 #
(这里使用符号 · 表示空格,用符号 # 表示换行符)因为 scanf 函数在寻找每个数的起始位置时会跳过空白字符,所以它可以成功读取这些数。
整数和浮点数的读取
scanf 函数通过什么样的规则来识别整数或浮点数呢?scanf 函数首先寻找正号啊或负号,然后读取数字直到读到一个非数值时才停止。当要求读入浮点数时,scanf 函数会寻找一个正号或负号(可选),然后是一串数字(可能含有小数点),再后是一个指数(可选)。在使用 scanf 函数时,转换说明符 %e,%f,%g 可以是互换的,这三种转换说明在识别浮点数方面都遵循相同的规则。
当 scanf 函数遇到不可能属于当前项的字符时,它会把此字符“放回到原处”,以便在描述下一个输入项或者下一次调用 scanf 函数时再次读入。
下面列出了 scanf 函数处理这组输入项的方法:
- 转换说明 %d 。第一个非空字符输入时12,因为整数里面可以有12,所以 scanf 函数接着读取下一个字符,即 - 。scanf 函数识别出字符 - 不能出现在整数内,所以把12存入变量 i 中,而把字符 - 放回原处(即放到24前面)。
- 转换说明 %d 。随后,scanf 函数读取字符 - ,2,4 和 .(小数点)。因为整数不能包含小数点,所以 scanf 函数把 -24 存入变量 j 中,而把字符 . 放回原处。
- 转换说明 %f 。接下来 scanf 函数读取字符 . ,3 和 - 。因为浮点数不能在数字后面有负号,所以 scanf 函数把 0.3 存入变量 x 中,而将字符 - 放回原处。
- 转换说明 %f 。最后,scanf 函数读取字符 - ,4,.,0,e,3 和换行符。因为浮点数不包含换行符,所以 scanf 函数把 -4.0 x 103 存入变量 y 中,而把换行符放回原处。
在上述例子中,scanf 函数能够把格式串中的每一个转换说明与一个输入项进行匹配。因为换行符没有读取,它将留给下一次 scanf 调用。
格式串中的普通字符
通过编写含有普通字符和转换说明的格式串能更进一步地理解模式匹配的概念。处理格式串中普通字符时,scanf 函数采取的动作依赖于这个字符是否为空白字符。
空白字符
当在格式串中遇到一个或多个连续的空白字符是,scanf 函数从输入中重复读空白字符直到遇到一个非空字符(把该字符“放回原处”)为止。格式串中空白字符的数量无关紧要,格式串中的一个空白字符可以与输入中任意数量的空白字符相匹配。(在格式串中包含空白字符并不意味着输入中必须包含空白字符。格式串中的一个空白字符可以与输入中的任意数量的空白字符相匹配,包括零个。)
其他字符
当在格式串中遇到非空白字符时,scanf 函数将把它与下一个输入字符进行比较。如果两个字符相匹配,那么 scanf 函数会放弃输入字符而继续处理格式串。如果两个字符不匹配,那么 scanf 函数会把不匹配的字符放回输入中,然后异常退出,而不进一步处理格式串或者从输入中读取字符。
例如,假设格式串是 “%d/%d" 。如果输入是
· 5 / · 96
在寻找整数时,scanf 函数会跳过第一个空格,把 %d 与 5 相匹配,把 / 和 / 相匹配,在寻找下一个整数时跳过一个空格,并且把 %d 与 96 相匹配。另一方面,如果输入是
· 5 · / · 96
scanf 函数会跳过第一个空格,把 %d 与 5 相匹配,然后试图把格式串中的 / 与输入中的空格相匹配。但二者不匹配,所以 scanf 函数会把空格放回原处,把字符
· / · 96
留给下一次 scanf 函数调用来读取。为了允许第一个数后边有空格,应使用格式串 " %d /%d "。
总结
详细介绍了怎么使用 printf 函数和 scanf 函数,两个函数都是模式匹配函数。首先两个函数都需要格式串,格式串当中分为普通字符和转换说明符,普通字符在格式串中原模原样的显示出来,而转换说明则是把待显示的值或变量按照格式要求输出到格式串占位符的位置当中。
scanf 函数输入中遇到一个或多个空白字符时,scanf 函数从输入中重复读取空白字符知道遇到一个非空字符为止。两个函数使用看起来简单但背后的读取和输入原理比较复杂,只有理解搞懂了规则使用起来就很容易。