← 目录 / 第三章 · 输入与输出 / 3.2 scanf 与 printf

3.2 scanf 与 printf

C 语言风格的输入输出函数,格式控制更简洁直观,在大量数据读写时效率也更高,是竞赛中另一种主流的 I/O 方式。

scanf 与 printf

scanf()printf() 是 C 语言的格式化输入输出函数,头文件是 <cstdio>,在 C++ 中同样可以直接使用。和 cin/cout 不同,它们靠"格式字符串"里的占位符来告诉编译器每个数据该按什么类型读写。

3.2.1 scanf 的工作原理与基础用法

语法:scanf("格式字符串", &变量);。格式字符串里每个 % 开头的占位符,对应一个要读入的变量,占位符的个数必须和后面变量的个数一一对应。

C++ · scanf 示例
1int year, month;
2scanf("%d %d", &year, &month); // 注意:变量前必须加 &
⚠️
别忘了 &cin >> x 不需要 &,但 scanf 要求变量前必须加取址符 &,因为 scanf 需要知道变量在内存中的地址才能把值写进去。忘记加 & 是初学者最常见的编译/运行错误之一(有的编译器只是警告,程序会在运行时崩溃或写入垃圾数据)。

3.2.2 常见类型格式符

格式符 数据类型 示例 说明
%c char scanf("%c", &ch) 读取单个字符
%d int scanf("%d", &n) 读取整数
%lld long long scanf("%lld", &n) 读取长整型
%f float scanf("%f", &x) 只能用于 float,不能用于 double
%lf double scanf("%lf", &x) 必须用 %lf,写成 %f 会读错数据
🚫
%f 和 %lf 不能混用:这是 scanf 里最经典的坑——读取 double 变量必须%lf,如果写成 %f,由于 scanf 是按照格式符去内存里"按字节写入"的,%f 和 %lf 写入的字节数不一样,会导致数据错乱,得到完全无意义的结果,但编译器往往不会报错。这一点和 printf 不同——printffloatdouble 都用 %f 就行(见 3.2.4 节),千万别把两边的规则记反。
💡
scanf 的返回值:scanf 返回成功读取的变量个数。例如 while(scanf("%d", &n) == 1) 可以持续读入直到输入结束(EOF),这在竞赛中处理不定量输入时非常实用。

3.2.3 printf 的工作原理与基础用法

语法:printf("格式化字符串", 变量1, 变量2, ...); 其中 % 开头的占位符会被对应变量的值替换,变量数量必须与占位符个数一致。和 scanf 不同,printf 输出时不需要加 &(因为只是读值输出,不需要往内存里写东西)。

C++ · printf 基本用法
1int age = 15;
2printf("我今年%d岁\n", age); // %d 对应整数
3float score = 98.5;
4printf("成绩是%f分\n", score); // %f 对应 float

3.2.4 数字位数与精度控制

格式符含义示例输出
%nd 整数占 n 位宽,右对齐,不足补空格 printf("%5d", 123)   123
%0nd 整数占 n 位宽,右对齐,不足补 0 printf("%05d", 1) 00001
%.nf float 保留 n 位小数(四舍五入) printf("%.3f", 3.14159f) 3.142
%.nf double 也用 %f(不是 %lf!),保留 n 位小数 printf("%.5f", 3.14) 3.14000
C++ · printf 格式控制示例
1printf("int指定宽度:%5d\n", 123); // " 123"
2printf("int用零填充:%05d\n", 123); // "00123"
3printf("float保留3位小数:%.3f\n", 1.23456f); // "1.235"
4printf("double保留3位小数:%.3f\n", 1.23456); // "1.235"(double 用 %f,不是 %lf)
📌
默认精度:printf 输出浮点数默认保留 6 位小数,不足 6 位以 0 补齐,超出 6 位的四舍五入。

3.2.5 与 cin/cout 的效率对比与优化

现在 cin/coutscanf/printf 都学过了,可以真正比较一下:这是竞赛中极其重要的知识点,大量 I/O 时选错方式可能直接超时!

🐢 默认 cin / cout
scanf / printf 慢很多,因为 C++ 为了兼容 C 的 I/O,默认让 cin / coutscanf / printf 的缓冲区同步,同步本身有开销。
加速后 cin / cout
关闭同步后,cin / cout 速度通常可以反超 scanf / printf,且代码更简洁易读。

优化方法:在 main() 开头加两行

C++ · I/O 加速模板
1int main()
2{
3 ios::sync_with_stdio(false); // 关闭 C/C++ I/O 同步
4 cin.tie(0); // 解除 cin 与 cout 的绑定
5 // ... 你的代码 ...
6 return 0;
7}
🚫
警告:关闭同步后,不要混用 cin / coutscanf / printf,否则可能导致输出顺序混乱。选择一种 I/O 方式并坚持使用。
🏆
竞赛习惯:输入输出量不大时用 cin / cout(代码简洁);大输入量时用 scanf / printf,或在加上两行加速代码后用 cin / cout

3.2.6 综合示例:时间格式化输出

读取小时和分钟,格式化输出为 HH:MM 的形式(不足两位时补零)。这是竞赛中常见的输入输出格式化需求。

C++ · 时间格式化输出
1#include <cstdio> // printf / scanf
2using namespace std;
3
4int main()
5{
6 int h, m;
7 scanf("%d%d", &h, &m); // 输入:9 5
8 printf("%02d:%02d\n", h, m); // 输出:09:05(不足两位补 0)
9 return 0;
10}

3.2.7 常见陷阱与易错点

把本节内容汇总成几条最容易踩坑的规则:

1
忘记加 &:scanf 读取变量时必须加取址符 &(字符数组/字符串除外,因为数组名本身就是地址),漏写是最常见的运行时错误来源。
2
scanf 读 double 要用 %lf,printf 输出 double 却用 %f:这是两个函数里唯一"不对称"的地方,非常容易记混。可以这样记:scanf 认真区分类型(分大小写字长),printf 一律用 %f 不区分 float/double
3
关闭同步后混用两种 I/O:调用了 ios::sync_with_stdio(false) 之后,如果代码里又同时出现了 cin/coutscanf/printf,两者的缓冲区各自独立,输出顺序可能会乱掉。一旦加速,就要在整个程序里只用一种 I/O 方式。
4
格式符与变量类型不匹配:比如用 %d 读一个 long long 变量,只会读进变量的一部分字节,结果完全不可预测。编译器通常不会报错,运行时却给出诡异的数字,一定要让格式符和变量类型严格对应。