C 语言风格的输入输出函数,格式控制更简洁直观,在大量数据读写时效率也更高,是竞赛中另一种主流的 I/O 方式。
3.2
scanf() 和 printf() 是 C 语言的格式化输入输出函数,头文件是 <cstdio>,在 C++ 中同样可以直接使用。和 cin/cout 不同,它们靠"格式字符串"里的占位符来告诉编译器每个数据该按什么类型读写。
语法:scanf("格式字符串", &变量);。格式字符串里每个 % 开头的占位符,对应一个要读入的变量,占位符的个数必须和后面变量的个数一一对应。
| 1 | int year, month; |
| 2 | scanf("%d %d", &year, &month); // 注意:变量前必须加 & |
&:cin >> x 不需要 &,但 scanf 要求变量前必须加取址符 &,因为 scanf 需要知道变量在内存中的地址才能把值写进去。忘记加 & 是初学者最常见的编译/运行错误之一(有的编译器只是警告,程序会在运行时崩溃或写入垃圾数据)。| 格式符 | 数据类型 | 示例 | 说明 |
|---|---|---|---|
| %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 会读错数据 |
scanf 里最经典的坑——读取 double 变量必须用 %lf,如果写成 %f,由于 scanf 是按照格式符去内存里"按字节写入"的,%f 和 %lf 写入的字节数不一样,会导致数据错乱,得到完全无意义的结果,但编译器往往不会报错。这一点和 printf 不同——printf 里 float 和 double 都用 %f 就行(见 3.2.4 节),千万别把两边的规则记反。scanf 返回成功读取的变量个数。例如 while(scanf("%d", &n) == 1) 可以持续读入直到输入结束(EOF),这在竞赛中处理不定量输入时非常实用。语法:printf("格式化字符串", 变量1, 变量2, ...); 其中 % 开头的占位符会被对应变量的值替换,变量数量必须与占位符个数一致。和 scanf 不同,printf 输出时不需要加 &(因为只是读值输出,不需要往内存里写东西)。
| 1 | int age = 15; |
| 2 | printf("我今年%d岁\n", age); // %d 对应整数 |
| 3 | float score = 98.5; |
| 4 | printf("成绩是%f分\n", score); // %f 对应 float |
| 格式符 | 含义 | 示例 | 输出 |
|---|---|---|---|
| %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 |
| 1 | printf("int指定宽度:%5d\n", 123); // " 123" |
| 2 | printf("int用零填充:%05d\n", 123); // "00123" |
| 3 | printf("float保留3位小数:%.3f\n", 1.23456f); // "1.235" |
| 4 | printf("double保留3位小数:%.3f\n", 1.23456); // "1.235"(double 用 %f,不是 %lf) |
printf 输出浮点数默认保留 6 位小数,不足 6 位以 0 补齐,超出 6 位的四舍五入。现在 cin/cout 和 scanf/printf 都学过了,可以真正比较一下:这是竞赛中极其重要的知识点,大量 I/O 时选错方式可能直接超时!
scanf / printf 慢很多,因为 C++ 为了兼容 C 的 I/O,默认让 cin / cout 与 scanf / printf 的缓冲区同步,同步本身有开销。cin / cout 速度通常可以反超 scanf / printf,且代码更简洁易读。| 1 | int main() |
| 2 | { |
| 3 | ios::sync_with_stdio(false); // 关闭 C/C++ I/O 同步 |
| 4 | cin.tie(0); // 解除 cin 与 cout 的绑定 |
| 5 | // ... 你的代码 ... |
| 6 | return 0; |
| 7 | } |
cin / cout 与 scanf / printf,否则可能导致输出顺序混乱。选择一种 I/O 方式并坚持使用。cin / cout(代码简洁);大输入量时用 scanf / printf,或在加上两行加速代码后用 cin / cout。读取小时和分钟,格式化输出为 HH:MM 的形式(不足两位时补零)。这是竞赛中常见的输入输出格式化需求。
| 1 | #include <cstdio> // printf / scanf |
| 2 | using namespace std; |
| 3 | |
| 4 | int 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 | } |
把本节内容汇总成几条最容易踩坑的规则:
scanf 读取变量时必须加取址符 &(字符数组/字符串除外,因为数组名本身就是地址),漏写是最常见的运行时错误来源。ios::sync_with_stdio(false) 之后,如果代码里又同时出现了 cin/cout 和 scanf/printf,两者的缓冲区各自独立,输出顺序可能会乱掉。一旦加速,就要在整个程序里只用一种 I/O 方式。%d 读一个 long long 变量,只会读进变量的一部分字节,结果完全不可预测。编译器通常不会报错,运行时却给出诡异的数字,一定要让格式符和变量类型严格对应。