此前我们借助 printf 单向打印信息。这一部分补充输入功能,使用 scanf 获取键盘传来的数据,让程序实现和用户交互。
printf:格式化输出
printf 的完整形态是:
printf("格式串", 值1, 值2, ...);格式串里的占位符(format specifier)和后面的参数按顺序一一对应:第一个 % 配第一个参数,第二个配第二个,以此类推。
#include <stdio.h>
int main(void) {
int year = 1972;
double pi = 3.14159;
printf("C 诞生于 %d 年。\n", year);
printf("圆周率 = %.2f\n", pi); // 保留 2 位小数
printf("%d + %d = %d\n", year, 1, year + 1); // 参数也可以是表达式
return 0;
}输出:
C 诞生于 1972 年。
圆周率 = 3.14
1972 + 1 = 1973最容易犯的错误:占位符数量和后面的参数数量不一致,或类型不匹配,都会打印出垃圾值,属于未定义行为。写几个、跟几个,需要一一对应。
常用占位符一览
| 占位符 | 用于 | 示例 |
|---|---|---|
%d | int | printf("%d", 42); |
%c | char | printf("%c", 'A'); |
%f | float / double | printf("%f", 3.14); |
%lf | double(scanf 读入时必用) | scanf("%lf", &x); |
%s | 字符串(char*) | printf("%s", "hi"); |
%x / %o | 十六进制 / 八进制 | printf("%x", 255); → ff |
%e / %g | 科学计数法 / 自动选短 | %.2e → 1.23e-06 |
控制宽度与精度
%f 默认打印 6 位小数,所以 3.14 会输出成 3.140000,噪音很大。用精度修饰符来压住它。格式是 %W.Nf:W 是总宽度(含小数点),N 是小数位数。
| 格式 | 含义 | 对 b=3.14159 |
|---|---|---|
%f | 默认 6 位小数 | 3.141590 |
%.0f | 只留整数部分(四舍五入) | 3 |
%.1f | 1 位小数 | 3.1 |
%.2f | 2 位小数(金额、百分比常用) | 3.14 |
%8.2f | 宽度 8 右对齐,2 位小数 | 3.14 |
%-8.2f | 宽度 8 左对齐,2 位小数 | 3.14 |
%08.2f | 宽度 8 前导补零,2 位小数 | 00003.14 |
一个容易误解的点:小数位是四舍五入,不是直接截断。比如 printf("%.2f", 3.145) 会输出 3.15,而 3.144 输出 3.14。
数值极大或极小时,用 %e(科学计数)或 %g(自动选更短的表示)能保住有效数字:
double v = 0.00000123;
printf("%.2f\n", v); // 0.00 ,信息全丢
printf("%.2e\n", v); // 1.23e-06 ,科学计数法scanf:从键盘读入
scanf 把输入按占位符解析,存入变量。最大区别是:必须在变量前加 &(取地址操作符),因为 scanf 需要知道把数据写到哪块内存:
#include <stdio.h>
int main(void) {
int age;
double height;
printf("请输入年龄和身高(空格或回车分隔):");
scanf("%d %lf", &age, &height);
printf("你 %d 岁,身高 %.1f 厘米。\n", age, height);
return 0;
}运行示例:
请输入年龄和身高(空格或回车分隔):18 170.5
你 18 岁,身高 170.5 厘米。这里 & 的意义留到指针章再细讲。现在只记死规则:scanf 里,整数/浮点/字符变量前都要加 &,唯一例外是字符串(它本身就是地址,后面讲)。
关于格式符再强调一次:double 用 %lf(printf 里 %f 和 %lf 都行,但 scanf 里 double 必须 %lf),int 用 %d,char 用 %c。
scanf 读字符串的两个易错点
字符串不加 &:
char name[50];
printf("你叫什么:");
scanf("%49s", name); // 最多读 49 个字符,给结尾 \0 留位置
printf("你好,%s!\n", name);两个坑:
%s遇到空格或换行就停止。输入Zhang San只会读到Zhang。- 不限制宽度时,输入超出缓冲区会内存越界,安全漏洞的常见来源。务必写
%49s这类上限(49 是数组大小减 1)。
更稳健的处理方式是 fgets,它能按行读取整行文本,后面字符串章节会介绍。
scanf 的返回值
scanf 会返回成功读入的字段数。用它判断输入是否合法:
int age;
if (scanf("%d", &age) != 1) {
printf("输入的不是整数!\n");
return 1;
}养成检查返回值的习惯,程序会健壮很多。
小结
printf 用于输出,scanf 用于输入。核心要点:占位符与变量匹配、普通变量前添加 &、字符串读取设置宽度限制。下一章讲解条件分支,介绍 if 与循环。