printf与scanf是兄弟?
printf
若程序跑起来之后只有几个数字会显得很生硬,那加入文字协调一下页面会不会好些呢?接下来就欢迎 printf 的加入。
基本概念
将格式化数据打印到标准输出
将按格式指向的 C 字符串写入标准输出 (stdout)。其中 f 代表 format (格式化),表示可以定制输出⽂本的格式。(即将参数⽂本输出到屏幕)
例:
#include<stdio.h>
int main()
{
printf("Hello my friend.");
return 0;
}
运行结果:
Hello my friend.
以上就是最直白的例子。当然不仅仅是英文,中文也同样可以输出。
另外,printf() 不会在行尾自动添加换行符,运⾏结束后,光标就停留在输出结束的地⽅,不会⾃动换行。若想要换行,可以在 Hello my friend. 之后加上 \n 。
如果对 \n 不太熟悉可参考 转义字符。
scanf
如果程序需要我们从键盘给变量赋值,那我们改如何操作呢?
这时候就该加入一个新成员—— scanf 。
基本概念
从标准输入中读取格式化数据
从 stdin 读取数据,并根据参数格式将它们存储到附加参数所指向的位置。
其他参数应指向格式字符串中相应格式说明符指定的类型的已分配对象。
例:
#include<stdio.h>
int main()
{
int score;
printf("请输入小燚的成绩:");
scanf("%d",&score);
printf("小燚同学的成绩为:%d",score);
return 0;
}
调试程序后输入100,最后结果为:
请输入小燚同学的成绩:100
小燚同学的成绩为:100
其实相当于 scanf 留了一个位置 score 给我们输入并存放数据。
注意:变量前⾯必须加上 & 运算符(指针变量除外),因为 scanf() 传递的不是值,⽽是地址,
即将变量 score的地址指向用户输⼊的值。
如果这⾥的变量是指针变量(⽐如字符串变量),那就不⽤加 & 运算符。
而 %d 是一个占位符,接下来将会详细介绍。
占位符
从表面意思来看就是用来占位置的符号,也就是格式占位,表示在该位置有输入或输出。
继以上例子,scanf(“%d”,&score); 当中有 %d ,就说明要用其他值来替换,即 score 。占位符的第一个字符一律为 % ,第二个字符表示占位符的类型,%d 就表示存放的是一个整数。
占位符列举
占位符 | 描述 |
---|---|
%a | 十六进制浮点数,字母输出为小写。 |
%A | 十六进制浮点数,字母输出为大写 |
%c | 字符 |
%d | 十进制整数 |
%e | 使⽤科学计数法的浮点数,指数部分的 e 为小写 |
%E | 使⽤科学计数法的浮点数,指数部分的 E 为大写 |
%i | 整数,基本等同于%d |
%f | 小数(包含float类型和double类型) |
%g | 6个有效数字的浮点数。整数部分⼀旦超过6位,就会⾃动转为科学计数法,指数部分的 e为⼩写 |
%G | 等同于 %g ,唯⼀的区别是指数部分的 E 为⼤写 |
%hd | ⼗进制 short int 类型 |
%ho | ⼋进制 short int 类型 |
%hx | ⼗六进制 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 | double 类型浮点数 |
%Lf | long double 类型浮点数 |
%n | 已输出的字符串数量。该占位符本⾝不输出,只将值存储在指定变量之中 |
%o | ⼋进制整数 |
%p | 指针 |
%s | 字符串 |
%u | ⽆符号整数(unsigned int) |
%x | ⼗六进制整数 |
%zd | size_t 类型 |
%% | 输出⼀个百分号 |
%[ ] | 在⽅括号中指定⼀组匹配的字符(⽐如 %[0-9] ),遇到不在集合之中的字符,匹配将会停止 |
printf 那些事
限定宽度
接下来就用例子来说明限定宽度
#include<stdio.h>
int main()
{
printf("%6d", 1234);
printf("\n");
printf("abcdef");
return 0;
}
运行结果:
1234
abcdef
printf(“%6d”, 1234);, %6d 其中的 6 表示这个占位符的宽度⾄少为6位。若对应的数不满六位,则系统默认右对齐,在数字左边补充两个空格,加上四个数字共6位。如果希望它左对齐也可以实现,只需在 % 后加上 - (负号)。
例:
#include<stdio.h>
int main()
{
printf("%-6d", 1234);
printf("\n");
printf("abcdef");
return 0;
}
运行结果:
1234
abcdef
注意:若对应的数超过了限定的宽度,那么限定的宽度就会失去作用,后面数字照常输出。
接下来是小数的限定宽度。
例:
#include<stdio.h>
int main()
{
printf("%12f", 123.45);
printf("\n");
printf("abcdefghijkl");
return 0;
}
运行结果:
123.450000
abcdefghijkl
printf(“%12f”, 123.45) ,这里限定的宽度是12,限定的类型是浮点型,小数的默认显示精度是小数点后6位,所以 123.45 后面补充四个 0 ,前面补充两个空格。同样是右对齐,若强制要左对齐就到%后加上 - (负号)。
注意:小数的小数点也占一位。
限定小数位数
小数点后想要保留几位小数,下面就来看例子。
例:
#include <stdio.h>
int main()
{
printf("%.3f", 0.5);
return 0;
}
运行结果:
0.500
只需要在%后加上 .8就可限定小数位数。
温馨提示:不要漏掉小数点哦。
限定宽度和限定小数位数也可以结合使用。
#include <stdio.h>
int main()
{
printf("%8.3f", 0.5);
printf("\n");
printf("abcdefgh");
return 0;
}
运行结果:
0.500
abcdefgh
以上程序没看懂的要仔细琢磨哟~
最小宽度和小数位数这两个限定值,都可以⽤ * 代替,通过 printf() 的参数传⼊。(注意对应关系)
#include <stdio.h>
int main()
{
printf("%*.*f",8,3,0.5);//同printf("%8.3f", 0.5);
printf("\n");
printf("abcdefgh");
return 0;
}
显示正负号
一般情况下,正数不显示 + (正号),只会对 - (负号)进行显示。
如果一定需要显示,就要在 % 后加上 + 。
#include <stdio.h>
int main()
{
printf("%-d\n", +12);
printf("%-d\n", -12);
return 0;
}
运行结果:
+12
-12
输出部分字符串
输出部分字符串和限制小数位数差别不大。
例:
#include <stdio.h>
int main()
{
printf("%.7s\n", "hello world");
printf("1234567");
return 0;
}
输出结果:
hello w
1234567
注意:在printf中,空格也算一个字符哦,千万别忽略了。
scanf那些事
scanf可以输入读入多个变量。
scanf("%d%f%d%c",&a,&b,&c,&d);
scanf( ) 处理数值占位符时,会⾃动过滤空白字符,包括空格、制表符、换行符等。
所以,用户输⼊的数据之间,有⼀个或多个空格不影响 scanf() 解读数据。另外,用户使⽤回车键,将输⼊分成几行,也不影响解读。
解读用户输⼊时,会从上⼀次解读遗留的第⼀个字符开始,直到读完缓存,或者遇到第⼀个不符合条件的字符为止。
#include <stdio.h>
int main()
{
int x;
float y;
scanf("%d", &x);
scanf("%f", &y);
return 0;// ⽤⼾输⼊ " -13.45e12# 0"
}
上面示例中, scanf() 读取用户输入时, %d 占位符会忽略起首的空格,从 - 处开始获取数据,读取到 -13 停下来,因为后面的 . 不属于整数的有效字符。这就是说,占位符 %d 会读到 -13 。
第⼆次调用 scanf() 时,就会从上⼀次停止解读的地方,继续往下读取。这⼀次读取的首字符是 . ,由于对应的占位符是 %f ,会读取到 .45e12 ,这是采用科学计数法的浮点数格式。后面的 # 不属于浮点数的有效字符,所以会停在这里。
scanf的返回值
scanf() 的返回值是⼀个整数,表示成功读取的变量个数。
如果没有读取任何项,或者匹配失败,则返回 0 。如果在成功读取任何数据之前,发⽣了读取错误或者遇到读取到⽂件结尾,则返回常量 EOF。
#include <stdio.h>
int main()
{
int a, b, c;
float d;
int r=scanf("%d%d%d%f", &a, &b, &c, &d);
printf("a=%d b=%d c=%d d=%f",a,b,c,d);
printf("\n");
printf("r=%d",r);
return 0;//用户输入2 4 7 5.6
}
运行结果:
2 4 7 5.6
a=2 b=4 c=7 d=5.600000
r=4
如果输⼊2个数后,按 ctrl+z ,提前结束输⼊:
在VS环境中按3次 ctrl+z ,才结束了输⼊,r=2 表示正确读取了2个数值。
如果⼀个数字都不输⼊,直接按3次 ctrl+z ,输出的r是-1,也就是EOF。
scnaf常用占位符
%c :字符。
%d :整数。
%f : float 类型浮点数。
%lf :double 类型浮点数。
%Lf :long double 类型浮点数。
%s :字符串。
%[ ] :在⽅括号中指定⼀组匹配的字符(比如 %[0-9] ),遇到不在集合之中的字符,匹配将会停⽌。
上⾯所有占位符之中,除了 %c 以外,都会⾃动忽略起首的空⽩字符。 %c 不忽略空白字符,总是返回当前第⼀个字符,无论该字符是否为空格。
如果要强制跳过字符前的空白字符,可以写成 scanf(" %c", &ch) ,即 %c 前加上⼀个空格,表示跳过零个或多个空白字符。
下面要特别说⼀下占位符 %s ,它其实不能简单地等同于字符串。它的规则是,从当前第⼀个非空白字符开始读起,直到遇到空⽩字符(即空格、换⾏符、制表符等)为止。
因为 %s 不会包含空白字符,所以无法用来读取多个单词,除非多个 %s ⼀起使用。这也意味着,scanf() 不适合读取可能包含空格的字符串,比如书名或歌曲名。另外, scanf() 遇到 %s 占位符,会在字符串变量末尾存储⼀个空字符 \0 。
scanf() 将字符串读⼊字符数组时,不会检测字符串是否超过了数组⻓度。所以,储存字符串时,很可能会超过数组的边界,导致预想不到的结果。为了防止这种情况,使用 %s 占位符时,应该指定读⼊字符串的最长长度,即写成 %8s ,其中的 8是可为其他整数,表示读取字符串的最大长度,后面的字符将被丢弃。
#include <stdio.h>
int main()
{
char name[9];
scanf("%8s", name);
return 0;
}
上面示例中, name 是⼀个长度为9的字符数组, scanf() 的占位符 %10s 表示最多读取用户输⼊的8个字符,后面的字符将被丢弃,这样就不会有数组溢出的⻛险了。
赋值忽略符
有时候用户不一定会像预期的格式一样输入,这时候我们就需要赋值忽略符。
#include <stdio.h>
int main()
{
int year,month,day;
scanf("%d-%d-%d", &year, &month, &day);
return 0;
}
在我们输入数据时%d之间的 - 也一定要输入。
上面示例中,如果用户输⼊ 2020-01-01 ,就会正确解读出年、月、日。问题是用户可能输⼊其他格式,比如 2020/01/01 ,这种情况下, scanf() 解析数据就会失败。
为了避免这种情况, scanf() 提供了⼀个赋值忽略符(assignment suppression character) *。只要把 * 加在任何占位符的百分号后面,该占位符就不会返回值,解析后将被丢弃。
#include <stdio.h>
int main()
{
int year = 0;
int month = 0;
int day = 0;
scanf("%d%*c%d%*c%d", &year, &month, &day);
return 0;
}
如上就不用担心用户输入的格式问题了。