本文最后更新于 34 天前,其中的信息可能已经有所发展或是发生改变。
一、printf是什么?
printf是C语言的输出函数,在C++中也可以使用。它与cout相比:
- cout:面向对象的流输出
- printf:函数式格式化输出
简单对比:
// cout方式
cout << "Hello, " << name << "! Age: " << age << endl;
// printf方式
printf("Hello, %s! Age: %d\n", name.c_str(), age);
二、printf的基本结构
2.1 基本格式
printf("格式字符串", 参数1, 参数2, ...);
示例:
#include <cstdio> // 需要这个头文件
// 或者 #include <stdio.h>
int main() {
int age = 20;
printf("I am %d years old.\n", age);
return 0;
}
输出:I am 20 years old.
2.2 格式说明符
这是printf最核心的部分,告诉函数如何格式化输出:
| 格式符 | 含义 | 对应C++类型 | 示例 |
|---|---|---|---|
%d | 十进制整数 | int | printf("%d", 123)→ 123 |
%f | 浮点数 | float, double | printf("%f", 3.14)→ 3.140000 |
%c | 单个字符 | char | printf("%c", 'A')→ A |
%s | 字符串 | char*, string | printf("%s", "Hello")→ Hello |
%lf | 双精度浮点 | double | printf("%lf", 3.14)→ 3.140000 |
三、printf的精度控制(重点)
3.1 浮点数精度控制
#include <cstdio>
int main() {
double num = 3.1415926535;
// 基本格式:%.nf
printf("默认: %f\n", num); // 3.141593
printf("0位: %.0f\n", num); // 3
printf("1位: %.1f\n", num); // 3.1
printf("2位: %.2f\n", num); // 3.14
printf("3位: %.3f\n", num); // 3.142
printf("4位: %.4f\n", num); // 3.1416
printf("5位: %.5f\n", num); // 3.14159
return 0;
}
注意:小数点前的.是必须的,表示精度控制。
3.2 与cout对比
#include <iostream>
#include <iomanip>
#include <cstdio>
using namespace std;
int main() {
double num = 3.1415926535;
// cout方式
cout << "cout方式:" << endl;
cout << fixed << setprecision(3) << num << endl; // 3.142
// printf方式
cout << "\nprintf方式:" << endl;
printf("%.3f\n", num); // 3.142
return 0;
}
可以看到,printf("%.3f", num)等价于 cout << fixed << setprecision(3) << num,但更简洁。
四、printf的宽度控制
4.1 基本宽度控制
#include <cstdio>
int main() {
int num = 123;
printf("无宽度: |%d|\n", num); // |123|
printf("宽度6: |%6d|\n", num); // | 123|(右对齐)
printf("宽度8: |%8d|\n", num); // | 123|
return 0;
}
4.2 左对齐
#include <cstdio>
int main() {
int num = 123;
printf("右对齐: |%6d|\n", num); // | 123|
printf("左对齐: |%-6d|\n", num); // |123 |
return 0;
}
记忆:-表示左对齐,没有-是右对齐。
4.3 补零
#include <cstdio>
int main() {
int num = 123;
printf("普通: %6d\n", num); // 123
printf("补零: %06d\n", num); // 000123
double price = 12.5;
printf("价格: $%06.2f\n", price); // $012.50
return 0;
}
五、综合示例:表格输出
5.1 用cout输出表格
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
cout << "用cout输出表格:" << endl;
cout << left << setw(10) << "姓名"
<< setw(8) << "年龄"
<< setw(10) << "分数" << endl;
cout << string(28, '-') << endl;
cout << left << setw(10) << "张三"
<< setw(8) << 20
<< fixed << setprecision(2) << setw(10) << 95.5 << endl;
cout << left << setw(10) << "李四"
<< setw(8) << 22
<< setw(10) << 88.75 << endl;
return 0;
}
5.2 用printf输出表格(更简洁)
#include <cstdio>
int main() {
printf("用printf输出表格:\n");
printf("%-10s %-8s %-10s\n", "姓名", "年龄", "分数");
printf("------------------------------\n");
printf("%-10s %-8d %-10.2f\n", "张三", 20, 95.5);
printf("%-10s %-8d %-10.2f\n", "李四", 22, 88.75);
return 0;
}
两种方法输出相同的结果:
姓名 年龄 分数
------------------------------
张三 20 95.50
李四 22 88.75
六、特殊格式控制
6.1 输出百分号
#include <cstdio>
int main() {
double progress = 0.756; // 75.6%
printf("进度: %.1f%%\n", progress * 100); // 进度: 75.6%
// 注意:%% 表示输出一个%字符
return 0;
}
6.2 八进制和十六进制
#include <cstdio>
int main() {
int num = 255;
printf("十进制: %d\n", num); // 255
printf("八进制: %o\n", num); // 377
printf("十六进制(小写): %x\n", num); // ff
printf("十六进制(大写): %X\n", num); // FF
printf("带前缀: 0x%X\n", num); // 0xFF
return 0;
}
6.3 科学计数法
#include <cstdio>
int main() {
double large_num = 1234567.89;
printf("默认: %f\n", large_num); // 1234567.890000
printf("科学计数: %e\n", large_num); // 1.234568e+06
printf("科学计数: %E\n", large_num); // 1.234568E+06
printf("自动选择: %g\n", large_num); // 1.23457e+06
return 0;
}
七、混合类型输出
7.1 同时输出不同类型
#include <cstdio>
#include <string>
using namespace std;
int main() {
int id = 101;
string name = "张三";
double score = 95.5;
char grade = 'A';
// cout方式
// cout << "学号:" << id << " 姓名:" << name
// << " 分数:" << score << " 等级:" << grade << endl;
// printf方式
printf("学号:%d 姓名:%s 分数:%.1f 等级:%c\n",
id, name.c_str(), score, grade);
return 0;
}
注意:printf输出string时需要用.c_str()转换为C风格字符串。
7.2 复杂的格式化
#include <cstdio>
int main() {
printf("商品价格表:\n");
printf("========================\n");
printf("%-15s %8s %10s\n", "商品名", "单价", "小计");
printf("------------------------\n");
printf("%-15s %8.2f %10.2f\n", "苹果", 5.99, 5.99 * 3);
printf("%-15s %8.2f %10.2f\n", "香蕉", 3.50, 3.50 * 2);
printf("%-15s %8.2f %10.2f\n", "橙子", 4.25, 4.25 * 5);
printf("------------------------\n");
printf("%-15s %18.2f\n", "总计", 5.99 * 3 + 3.50 * 2 + 4.25 * 5);
return 0;
}
输出:
商品价格表:
========================
商品名 单价 小计
------------------------
苹果 5.99 17.97
香蕉 3.50 7.00
橙子 4.25 21.25
------------------------
总计 46.22
八、常见错误与注意事项
8.1 类型不匹配
// 错误示例
int age = 20;
printf("Age: %f\n", age); // 错误!应该用%d而不是%f
// 正确
printf("Age: %d\n", age);
8.2 忘记字符串转换
#include <string>
using namespace std;
// 错误示例
string name = "Tom";
printf("Hello, %s\n", name); // 错误!需要.c_str()
// 正确
printf("Hello, %s\n", name.c_str());
8.3 参数个数不匹配
// 错误:少了参数
printf("Name: %s, Age: %d\n", "Tom"); // 缺少age参数
// 错误:多了参数
printf("Name: %s\n", "Tom", 20); // 20没用到
8.4 转义字符
printf("第一行\n第二行\n"); // \n 换行
printf("制表符:\t内容\n"); // \t 制表符
printf("引号:\"Hello\"\n"); // \" 输出引号
printf("反斜杠:\\n\n"); // \\ 输出反斜杠
九、与cout的详细对比
9.1 简单对比
#include <iostream>
#include <iomanip>
#include <cstdio>
using namespace std;
int main() {
int a = 10;
double b = 3.14159;
string c = "test";
// cout
cout << "a=" << a << ", b=" << fixed << setprecision(3)
<< b << ", c=" << c << endl;
// printf
printf("a=%d, b=%.3f, c=%s\n", a, b, c.c_str());
return 0;
}
9.2 性能对比
#include <iostream>
#include <cstdio>
#include <chrono>
using namespace std;
using namespace chrono;
int main() {
int n = 100000;
// 测试cout
auto start1 = high_resolution_clock::now();
for (int i = 0; i < n; i++) {
cout << "i=" << i << ", value=" << 3.14159 << endl;
}
auto end1 = high_resolution_clock::now();
// 测试printf
auto start2 = high_resolution_clock::now();
for (int i = 0; i < n; i++) {
printf("i=%d, value=%f\n", i, 3.14159);
}
auto end2 = high_resolution_clock::now();
auto duration1 = duration_cast<milliseconds>(end1 - start1);
auto duration2 = duration_cast<milliseconds>(end2 - start2);
cout << "\n性能对比:" << endl;
cout << "cout耗时: " << duration1.count() << "ms" << endl;
cout << "printf耗时: " << duration2.count() << "ms" << endl;
return 0;
}
一般来说:printf比cout快,特别是在大量输出时。
十、实用技巧
10.1 用sprintf格式化到字符串
#include <cstdio>
#include <string>
#include <iostream>
using namespace std;
int main() {
char buffer[100];
int year = 2024;
int month = 3;
int day = 15;
// 格式化到字符串
sprintf(buffer, "%04d-%02d-%02d", year, month, day);
string date_str = buffer; // 转换为string
cout << "日期: " << date_str << endl; // 2024-03-15
return 0;
}
10.2 动态构建格式字符串
#include <cstdio>
int main() {
int width = 10;
int precision = 3;
double value = 123.456789;
// 动态构建格式字符串
char format[20];
sprintf(format, "%%%d.%df", width, precision);
printf("格式字符串: %s\n", format); // %10.3f
printf(format, value); // 123.457
printf("\n");
return 0;
}
10.3 清空输出缓冲区
#include <cstdio>
int main() {
printf("正在处理...");
fflush(stdout); // 立即输出,不清缓冲区会等到换行才输出
// 模拟耗时操作
for (int i = 0; i < 100000000; i++);
printf("完成!\n");
return 0;
}
十一、从cout迁移到printf的速查表
| 你想输出… | cout写法 | printf写法 | 说明 |
|---|---|---|---|
| 整数 | cout << num | printf("%d", num) | |
| 浮点数(3位) | fixed << setprecision(3) << num | printf("%.3f", num) | 常用 |
| 字符 | cout << ch | printf("%c", ch) | |
| 字符串 | cout << str | printf("%s", str.c_str()) | 注意.c_str() |
| 换行 | cout << endl | printf("\n") | |
| 左对齐宽10 | left << setw(10) << num | printf("%-10d", num) | |
| 右对齐宽10 | right << setw(10) << num | printf("%10d", num) | |
| 补零宽5 | setfill('0') << setw(5) << num | printf("%05d", num) |
十二、练习与测试
练习1:学生成绩单
#include <cstdio>
int main() {
// 用printf输出以下成绩单:
// 学号 姓名 语文 数学 英语 平均分
// 001 张三 85.5 92.0 88.5 88.7
// 002 李四 78.0 85.5 90.0 84.5
printf("%-6s %-8s %-6s %-6s %-6s %-8s\n",
"学号", "姓名", "语文", "数学", "英语", "平均分");
printf("--------------------------------------------\n");
printf("%-6s %-8s %-6.1f %-6.1f %-6.1f %-8.1f\n",
"001", "张三", 85.5, 92.0, 88.5, (85.5+92.0+88.5)/3);
printf("%-6s %-8s %-6.1f %-6.1f %-6.1f %-8.1f\n",
"002", "李四", 78.0, 85.5, 90.0, (78.0+85.5+90.0)/3);
return 0;
}
练习2:计算圆的相关值
#include <cstdio>
#include <cmath>
#define PI 3.1415926535
int main() {
double radius = 5.5;
double area = PI * radius * radius;
double circumference = 2 * PI * radius;
printf("半径为 %.1f 的圆:\n", radius);
printf("面积 = %.4f\n", area);
printf("周长 = %.4f\n", circumference);
return 0;
}
十三、总结
为什么学习printf?
- 简洁:一行代码完成复杂格式化
- 快速:执行效率通常比cout高
- 灵活:格式化控制非常强大
- 通用:C/C++都能用,很多老代码在用
给C++学习者的建议:
- 先掌握cout:因为它是C++的标准,类型安全
- 再学printf:需要时使用,特别是需要精确格式化时
- 混合使用:简单输出用cout,复杂格式化用printf
核心语法记忆:
// 基本模板
printf("格式字符串", 参数1, 参数2, ...);
// 常用格式
%d // 整数
%.nf // 浮点数,n位小数
%s // 字符串
%c // 字符
// 宽度和对齐
%10d // 宽10,右对齐
%-10d // 宽10,左对齐
%010d // 宽10,补零
最后建议:
从今天开始,在需要格式化输出时尝试用printf代替cout,你会发现它真的很方便!特别是处理表格、对齐、精度控制时,代码会比cout简洁很多。