Blaze,你终于也开始学C了哈哈哈哈XD
我们已经知道,C语言会使用scanf()
和printf()
来进行输入/输出。关于这两个函数,我们在这里再做深入的了解。
printf()函数
printf()
函数会把所有类型的数据转换成字符串,并打印在屏幕上。它的标准格式是:
printf("格式字符串",待打印值1,待打印值2……);
其中,格式字符串需要包括“实际打印的字符”与“转换说明”。而且转换说明的数量、类型都要与后面的打印值相匹配。打印值可以是常量、变量或者合法的表达式。如果转换说明与打印值不匹配,编译器不会报错。printf()
函数本质上使用的是值。
特别需要注意的是:由于函数内的转换说明都以“%”作为开头,所以我们在打印“%”号时,需要在前面再加一个%。
例如,要打印“存在【变量值】%的风险”这个语句。就要写成:
printf("存在%d%%的风险。",risk);
修饰转换说明
如果在%和转换字符中插入修饰符,就可以修饰基本的转换说明。如果要插入多个修饰符,就应当遵循一定的顺序。我们来详细讲解这些说明。
标记
常见的转换说明标记有五种:**减号、加号、空格、井号和0。**它们的含义如下:
- 减号:待打印项左对齐;
- 加号:在有符号值的前面显示符号。正值显示正号,负值显示负号;
- 空格:同加号类似,不过正值不显示符号而显示空格;
- 井号:把结果转换成另一种显示形式。例如:在打印十六进制数时,使用井号可以在数字前面附上
0x
前缀。在浮点格式转换说明前加上井号,就能使函数固定打印一位小数; - 0:代替空格填充字段宽度。在前面加上几个0,打印时就会在前面多出几个0。
数字与.数字
在%号后插入数字,代表最小字段宽度。即在打印时,系统会使用规定的字段宽度打印。如果字段宽度不足以容纳打印的值,系统会自动扩宽字段宽度。
在%号后插入.数字
,代表精度。具体如下:
- 对于
%e/%f
转换,表示小数位数。 - 对于
%s
转换,表示打印字符的最大数量 - 对于
%d
转换,表示最小位数。
我们通过下面的程序来演示修饰转换说明的效果:
//modify.c 演示修饰转换说明
#include <stdio.h>
#define NUM 123
int main(void)
{
float fl = 1.23456;
printf("|%d|\n", NUM);
printf("|%2d|\n", NUM);//数字
printf("|%10d|\n", NUM);//长数字
printf("|%10f|\n", fl);
printf("|%.2f|\n", fl);//.数字
printf("|%10.2f|\n", fl);//数字和.数字共用
printf("|%+10d|\n", NUM);//加号
printf("|%-10d|\n", NUM);//减号
printf("|%#x|\n", NUM);//井号
printf("|%05d|", NUM);//0
return 0;
}
试运行,输出结果如下:
|123|
|123|
| 123|
| 1.234560|
|1.23|
| 1.23|
| +123|
|123 |
|0x7b|
|00123|
我们来详细解释:
- 第一行未作任何变化;
- 第二行限制了最小字段宽度,但由于字段宽度不能容纳下所需要打印的值,所以编译器自动扩宽了字段宽度;
- 第三行限制了最小字段宽度,所以程序打印这个值的总宽度是10;
- 第四行同上;
- 第五行限制了精度,所以打印时精确到小数点后2位,这里遵循四舍五入;
- 第六行是数字和.数字共用。在同时使用的时候,要遵循这样的规则;
- 第七行使用了加号,所以在有符号数
NUM
的前面加上了正号; - 第八行使用了减号,所以编译器将要打印的值左对齐,从左打印;
- 第九行使用了减号,所以在十六进制数
7b
的前面加上了0x
前缀。 - 第十行使用了0,所以在打印时前面加上了相应个数的0。
(扩展:当使用%e转换说明时,.数字代表指数)
在字符串中,也有着相似的修饰方法:
//string_modify.c 演示字符串修饰转换说明
#include <stdio.h>
#define NAME "Briue Rua"
int main(void)
{
printf("|%2s|\n", NAME);
printf("|%12s|\n", NAME);
printf("|%12.3s|\n", NAME);
printf("|%-12.3s|\n", NAME);
return 0;
}
运行结果如下:
|Briue Rua|
| Briue Rua|
| Bri|
|Bri |
可以发现,数字仍然代表最小字段宽度,.数字则是代表打印的字符个数。
转换说明不匹配
通常,打印一个值时,使用多种类型的转换说明都是可行的。例如,打印10这个整数,可以使用%d,%x等转换说明。但是,转换说明如果与值不匹配,会发生一些问题。这些问题的原理较为复杂。可以参考这里。
printf()的返回值
绝大多数函数都有返回值。printf()
也不例外。它的返回值是打印字符的个数(包括非打印字符)。如果出现输出错误,则返回一个负值。
scanf()函数
scanf()
是较为常用的输入函数。我们只能从键盘上输入字符。例如,当我们输入整数“123”时。我们实际输入的是字符1、2、3。scanf()的目的就是要把字符串转换成对应类型的数据,这和printf()函数正好相反。它的标准格式是:
scanf("%数据类型的转换说明",&变量名)//当变量是字符数组时,不要使用&。
在 C 语言中,数组名本身就代表数组首元素的地址,也就是数组在内存中的起始地址。因此,当使用 scanf
函数读取字符串并存储到字符数组中时,不需要使用 &
来取地址。
我们已经知道,scanf()函数在遇到空格、换行符或制表符时会停止读取输入。但是%c
除外。我们将在后面详细讨论这个问题。
scanf()中的转换说明,用来指示scanf()函数把输入的字符解释成什么数据。它们的用法大致与printf()函数相同。但是把数据解释成double类型时要使用**l
**修饰符。
scanf()输入的原理
我们现在详细研究scanf()函数。假设有以下语句:
int num;
scanf("%d",&num);
这两条语句要求scanf()函数根据%d
转换说明读取一个整数。接下来,scanf()函数每次读入一个整数(或者正负号),并把它放入缓冲区,这个过程直到遇见非数字字符为止。比如,假如用户的输入是123q456
,那么scanf()就会逐一读取1、2、3,直到遇到q,它就会认为已经读到了整数的末尾,于是停止读入。
然后,scanf()会把非数字字符放回输入。程序在下一次读取输入时,首先遇到的是上一次留下的非数字字符。
最后,scanf()将计算缓冲区内已读取到的数字的值,并将它存储进指定的变量中。
放回输入后,如果下一个scanf()仍然使用%d
转换说明,那么它遇到的第一个字符不是4,而是q。所以,它会停下并将数据放回输入中,假如后面的scanf()都使用%d
转换说明,就会一直无法读取输入。
使用其他转换说明的方式和上述类似。不过读取的范围会有所不同*(例如%x会读取大小写的A~F)*。在使用%s
转换类型时,scanf()函数会读取除空白字符外的所有字符(也就是一个中间无空格的单词)。
假如我们使用数字修饰符限定转换说明,那么scanf()会在字段终止或遇到不满足条件的字符时(满足其一)停止。
scanf()的其他特性
scanf()允许把普通字符放在格式字符串中。除了空格字符以外,输入的字符串必须与格式字符串严格匹配。例如:
scanf("%d,%f",&num,&fl);
scanf()函数会将其解释成:用户会先输入一个整数,再输入一个逗号,最后输入一个浮点数。用户必须以这样的方式输入两个数字,否则程序便不会正常运行。
如果在格式字符串中把空格放到%c
的前面,scanf()函数就会从第一个非空白字符处读取(而不是从第一个字符处开始)。
scanf()的返回值
scanf()返回成功读取的项数。如果没有读取到任何项,而且读取到的字符串和转换说明不匹配,那么就会返回0。我们将在之后详细讨论scanf()函数返回值的运用(处理不匹配的输入)。
星号修饰符
在printf()函数中,我们在转换说明中加入*
修饰符,能够代替数字和.数字修饰符。但是需要在后面指示*
代替的数字。例如:
printf("%*.*f",width,precision,fl);
在scanf()函数中,*
修饰符会使得scanf()函数跳过相应的输入项。例如:
scanf("%*d %d",&num);
它的意思是:跳过第一个用户注入的整数,把第二个整数赋给num。
关于星号修饰符的更多内容见此。