C++ 标准库 :掌握输入输出的基石
在 C++ 编程的世界里,输入输出操作是程序与外界沟通的桥梁。虽然我们常常用 cin 和 cout 来处理数据,但它们背后其实依赖于更底层的机制。今天我们要聊的,正是这个常被忽视却极其重要的组成部分——C++ 标准库 <cstdio>。
你可能会问:<cstdio> 和 iostream 有什么区别?简单来说,<cstdio> 提供的是 C 风格的输入输出函数,比如 printf 和 scanf,它们效率高、格式控制灵活,尤其适合处理大量数据或需要精确控制输出格式的场景。而 iostream 更偏向面向对象,使用流操作符,更适合初学者快速上手。
但别误会,<cstdio> 并不是过时的技术。相反,它在嵌入式开发、高性能计算、文件处理等场景中依然占据重要地位。掌握它,不仅让你的代码更具选择性,还能帮助你理解底层 I/O 的工作原理。
接下来,我们就从基础用法讲起,一步步深入 <cstdio> 的核心功能。
格式化输出:printf 的强大之处
printf 是 <cstdio> 中最核心的函数之一,它的作用是按照指定格式输出数据到标准输出(通常是控制台)。它不像 cout 那样依赖流操作符,而是通过格式字符串来控制输出内容。
基本语法与占位符
#include <cstdio>
int main() {
int age = 25;
double salary = 7500.50;
char name[] = "张三";
// 使用 printf 输出不同类型的数据
printf("姓名:%s,年龄:%d,月薪:%.2f 元\n", name, age, salary);
return 0;
}
代码注释:
printf的第一个参数是格式字符串,其中%s表示字符串类型,%d表示整数,%.2f表示浮点数并保留两位小数。\n是换行符,确保输出后光标下移。- 多个变量按顺序传递,与格式符一一对应,顺序不能错。
💡 小贴士:
%.2f中的.2指定小数点后保留两位,如果你写成%.0f,结果就是整数形式(如 7500),这在财务计算中非常实用。
常见格式符对照表
| 格式符 | 用途 | 示例输入 | 输出结果 |
|---|---|---|---|
%d |
十进制整数 | 123 | 123 |
%u |
无符号整数 | 456 | 456 |
%f |
浮点数(默认6位小数) | 3.14159 | 3.141590 |
%.2f |
浮点数保留两位小数 | 3.14159 | 3.14 |
%s |
字符串 | "Hello" | Hello |
%c |
单个字符 | 'A' | A |
%x |
十六进制整数 | 255 | ff |
%p |
指针地址 | &var | 0x7ffee4b0d000 |
这个表格是学习 printf 的必备参考。记住:格式符必须与变量类型匹配,否则可能引发未定义行为。
格式化输入:scanf 的精准控制
如果说 printf 是“说”,那么 scanf 就是“听”。它从标准输入(通常是键盘)读取数据,并根据格式字符串解析成对应类型。
#include <cstdio>
int main() {
int age;
double height;
char name[50];
// 从用户输入读取数据
printf("请输入你的姓名、年龄和身高:\n");
scanf("%s %d %lf", name, &age, &height);
// 输出读取结果
printf("你好,%s!你今年 %d 岁,身高 %.1f 米。\n", name, age, height);
return 0;
}
代码注释:
scanf的第一个参数是格式字符串,与printf类似。- 传递变量时要加取地址符
&(如&age),因为scanf需要直接修改内存值。 "%s"用于读取字符串,但要注意缓冲区溢出问题(后续会讲)。"%lf"是double类型的正确格式符,别写成%f(那是float)。
⚠️ 重要提醒:
scanf不会自动检查输入长度,如果输入的字符串超过缓冲区大小,就会造成缓冲区溢出,这是常见的安全隐患。建议使用scanf_s(Windows)或fgets+sscanf替代。
字符串与字符处理:gets、putchar、getchar
虽然 <cstdio> 提供了基础的字符处理函数,但部分函数已被废弃或存在风险,使用时需格外小心。
使用 getchar 和 putchar 逐字符读写
#include <cstdio>
int main() {
char ch;
printf("请输入一个字符:");
// 逐个读取字符
ch = getchar();
// 输出字符
putchar(ch);
printf(" 已被读取。\n");
return 0;
}
代码注释:
getchar()从输入流读取一个字符(包括空格和回车),返回int类型(因为要能表示 EOF)。putchar(ch)输出一个字符。- 适合处理字符流,比如过滤输入、统计字符个数等。
警惕 gets 的危险性
#include <cstdio>
int main() {
char buffer[20];
printf("请输入一段文字(不超过19个字符):");
gets(buffer); // ❌ 危险!不推荐使用
printf("你输入的是:%s\n", buffer);
return 0;
}
代码注释:
gets会无限制地读取输入,直到遇到换行符或文件结束符。- 它不检查目标缓冲区大小,极易导致缓冲区溢出,是安全漏洞的常见来源。
- 强烈建议:改用
fgets(buffer, sizeof(buffer), stdin),它能指定最大读取长度。
文件操作:fopen、fprintf、fscanf、fclose
<cstdio> 不仅支持控制台输入输出,还能操作文件。这使得它在日志记录、数据持久化等场景中非常有用。
#include <cstdio>
int main() {
FILE* file = fopen("data.txt", "w"); // 打开文件用于写入
if (file == NULL) {
printf("文件打开失败!\n");
return 1;
}
// 写入数据到文件
fprintf(file, "学号:1001\n");
fprintf(file, "姓名:李四\n");
fprintf(file, "成绩:%.1f\n", 89.5);
fclose(file); // 关闭文件,释放资源
// 重新打开文件读取内容
file = fopen("data.txt", "r");
if (file == NULL) {
printf("文件读取失败!\n");
return 1;
}
char line[100];
while (fgets(line, sizeof(line), file) != NULL) {
printf("%s", line);
}
fclose(file);
return 0;
}
代码注释:
fopen打开文件,模式"w"表示写入(会覆盖原内容),"r"表示只读。fprintf和fscanf是printf和scanf的文件版本。fgets安全读取一行,比gets更安全。fclose必须调用,否则可能导致数据丢失或资源泄漏。
💡 实用建议:在实际项目中,建议将文件操作封装成函数,避免重复代码,提升可维护性。
实际应用案例:学生成绩统计程序
我们来写一个完整的程序,演示如何用 <cstdio> 实现学生成绩的读取、处理与输出。
#include <cstdio>
#include <cstring>
#define MAX_STUDENTS 100
#define MAX_NAME_LEN 50
struct Student {
char name[MAX_NAME_LEN];
int id;
double score;
};
int main() {
Student students[MAX_STUDENTS];
int n = 0;
printf("请输入学生人数:");
scanf("%d", &n);
// 读取学生信息
for (int i = 0; i < n; i++) {
printf("请输入第 %d 个学生的姓名、学号和成绩:\n", i + 1);
scanf("%s %d %lf", students[i].name, &students[i].id, &students[i].score);
}
// 输出所有学生信息
printf("\n--- 学生成绩列表 ---\n");
printf("%-10s %-8s %-8s\n", "姓名", "学号", "成绩");
for (int i = 0; i < n; i++) {
printf("%-10s %-8d %-8.1f\n", students[i].name, students[i].id, students[i].score);
}
// 统计平均分
double total = 0;
for (int i = 0; i < n; i++) {
total += students[i].score;
}
double avg = total / n;
printf("\n平均成绩:%.2f\n", avg);
return 0;
}
代码注释:
- 使用结构体
Student封装学生信息,便于管理。 %-10s表示左对齐输出,宽度为10个字符,避免字段错位。scanf读取字符串时不加&,因为数组名本身就是地址。- 程序逻辑清晰,适合初学者理解“输入-处理-输出”流程。
总结与建议
C++ 标准库 <cstdio> 虽然源自 C 语言,但至今仍是现代 C++ 项目中不可或缺的一部分。它提供了高效、灵活的格式化输入输出能力,尤其适合处理大量数据、文件操作或性能敏感的场景。
通过本文的学习,你应该已经掌握了:
printf和scanf的基本用法与格式符- 如何安全地读写文件
- 逐字符输入输出的技巧
- 如何避免缓冲区溢出等常见陷阱
在实际开发中,建议根据场景合理选择工具:简单交互用 cout,复杂格式用 printf,文件操作用 fopen 系列函数。不要盲目追求“现代化”,有时候,老技术恰恰是最可靠的。
记住:理解底层,才能驾驭高级。 深入 <cstdio>,不仅是在学一个库,更是在构建对 C++ I/O 机制的完整认知。
最后,无论你是在写一个小游戏、处理日志文件,还是开发嵌入式系统,<cstdio> 都会是你值得信赖的伙伴。多练习,多思考,你的代码会越来越稳健。