是什么
📚 C 语言标准输入输出库的 C++ 版本。
📚 C 语言里叫 <stdio.h>,C++ 里改叫 <stdio.h>,所有函数都放在 std 命名空间里。
📚 它提供了 printf、scanf、 fopen 等经典的格式化 IO 函数。
类比
🏠 如果说 <iostream> 是"用流的方式搬数据"(cin >> x,cout << x), 那 <stdio.h> 就是"用格式化字符串搬数据"(printf("%d", x))。
🏠 iostream 类型安全、可扩展;
🏠 cstdio 更简洁、格式化更灵活、运行更快, 但写错格式化符会出 bug。
常用函数(每个函数附完整示例)
控制台输入输出
printf(格式, 参数...) —— 格式化输出到屏幕
#include <time.h>
#include <stdio.h>
int main() {
struct tm t = {};
t.tm_year = 2024 - 1900;
t.tm_mon = 0;
t.tm_mday = 1;
t.tm_hour = 12;
t.tm_min = 0;
t.tm_sec = 0;
time_t timestamp = mktime(&t);
printf("2024年1月1日12:00的时间戳:%d\n", timestamp);
printf("对应时间:%d", ctime(×tamp));
return 0;
}
#include <time.h>
#include <stdio.h>
int main() {
struct tm t = {};
t.tm_year = 2024 - 1900; // 年(从1900开始)
t.tm_mon = 0; // 月(0-11,0是1月)
t.tm_mday = 1; // 日(1-31)
t.tm_hour = 12;
t.tm_min = 0;
t.tm_sec = 0;
time_t timestamp = mktime(&t);
printf("2024年1月1日12:00的时间戳:%d\n", timestamp);
printf("对应时间:%d", ctime(×tamp));
return 0;
}scanf(格式, 地址...) —— 从键盘格式化读入(变量要加 &)
#include <stdio.h>
int main() {
printf("年龄:%d,成绩:%.1f\n", 20, 95.5);
return 0;
}
#include <stdio.h>
int main() {
printf("年龄:%d,成绩:%.1f\n", 20, 95.5);
return 0;
}puts(字符串) —— 输出字符串并自动换行
#include <stdio.h>
int main() {
int x;
printf("请输入一个整数:");
scanf("%d", &x);
printf("你输入的是:%d\n", x);
return 0;
}
#include <stdio.h>
int main() {
int x;
printf("请输入一个整数:");
scanf("%d", &x);
printf("你输入的是:%d\n", x);
return 0;
}gets(字符串) —— 读一行(已废弃,不安全,勿用,用 fgets)
#include <stdio.h>
int main() {
puts("Hello World");
return 0;
}
#include <stdio.h>
int main() {
puts("Hello World"); // 输出后自动换行
return 0;
}getchar() —— 读一个字符,返回 int(EOF 时为 -1)
#include <stdio.h>
int main() {
printf("gets()已废弃,请使用fgets()\n");
return 0;
}
#include <stdio.h>
int main() {
// 不要使用 gets(),缓冲区无边界检查,会溢出
printf("gets()已废弃,请使用fgets()\n");
return 0;
}putchar(字符) —— 输出一个字符
#include <stdio.h>
int main() {
printf("请输入一个字符:");
int c = getchar();
putchar(c);
putchar('\n');
return 0;
}
#include <stdio.h>
int main() {
printf("请输入一个字符:");
int c = getchar();
putchar(c);
putchar('\n');
return 0;
}文件操作
🎯 fopen(文件名, 模式) —— 打开文件,成功返回 FILE*,失败返回 NULL
#include <stdio.h>
int main() {
putchar('A');
putchar('\n');
return 0;
}
#include <stdio.h>
int main() {
putchar('A');
putchar('\n');
return 0;
}fopen(文件名, 模式) —— 打开文件,成功返回 FILE*,失败返回 NULL
📌 模式:"r"读、"w"写(覆盖)、"a"追加、"r+"读写、"w+"读写(覆盖)、"a+"读写(追加)
📌 加 b 表示二进制:"rb"、"wb"、"ab"
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "r");
if (fp) {
printf("文件打开成功\n");
fclose(fp);
} else {
printf("文件打开失败\n");
}
return 0;
}
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "r");
if (fp) {
printf("文件打开成功\n");
fclose(fp);
} else {
printf("文件打开失败\n");
}
return 0;
}fclose(文件指针) —— 关闭文件,刷新缓冲区
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "w");
if (fp == NULL) {
printf("文件打开失败\n");
return 1;
}
fprintf(fp, "Hello World\n");
fclose(fp);
printf("文件写入成功\n");
return 0;
}
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "w");
if (fp == NULL) {
printf("文件打开失败\n");
return 1;
}
fprintf(fp, "Hello World\n");
fclose(fp);
printf("文件写入成功\n");
return 0;
}fprintf(文件, 格式...) —— 格式化写入文件
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "w");
if (fp) {
fprintf(fp, "hello\n");
fclose(fp);
printf("文件已关闭\n");
}
return 0;
}
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "w");
if (fp) {
fprintf(fp, "hello\n");
fclose(fp);
printf("文件已关闭\n");
}
return 0;
}fscanf(文件, 格式...) —— 从文件格式化读取
#include <stdio.h>
int main() {
FILE* fp = fopen("scores.txt", "w");
if (fp) {
fprintf(fp, "%s 考了 %d 分\n", "小明", 88);
fclose(fp);
}
return 0;
}
#include <stdio.h>
int main() {
FILE* fp = fopen("scores.txt", "w");
if (fp) {
fprintf(fp, "%s 考了 %d 分\n", "小明", 88);
fclose(fp);
}
return 0;
}fgets(缓冲区, 大小, 文件) —— 读一行(含换行符),最多读 大小-1 个字符
#include <stdio.h>
int main() {
FILE* fp = fopen("scores.txt", "r");
if (fp) {
int score;
fscanf(fp, "%d", &score);
printf("分数:%d\n", score);
fclose(fp);
}
return 0;
}
#include <stdio.h>
int main() {
FILE* fp = fopen("scores.txt", "r");
if (fp) {
int score;
fscanf(fp, "%d", &score);
printf("分数:%d\n", score);
fclose(fp);
}
return 0;
}fputs(字符串, 文件) —— 写字符串(不自动加换行)
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "r");
if (fp) {
char buf[256];
fgets(buf, sizeof(buf), fp);
printf("读到:%s", buf);
fclose(fp);
}
return 0;
}
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "r");
if (fp) {
char buf[256];
fgets(buf, sizeof(buf), fp);
printf("读到:%s", buf);
fclose(fp);
}
return 0;
}fread(缓冲区, 大小, 个数, 文件) —— 二进制读,返回实际读到的元素数
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "w");
if (fp) {
fputs("一行文字\n", fp);
fclose(fp);
}
return 0;
}
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "w");
if (fp) {
fputs("一行文字\n", fp);
fclose(fp);
}
return 0;
}fwrite(缓冲区, 大小, 个数, 文件) —— 二进制写,返回实际写入的元素数
#include <stdio.h>
int main() {
FILE* fp = fopen("data.bin", "rb");
if (fp) {
int arr[10];
size_t n = fread(arr, sizeof(int), 10, fp);
printf("读到 %zu 个整数\n", n);
fclose(fp);
}
return 0;
}
#include <stdio.h>
int main() {
FILE* fp = fopen("data.bin", "rb");
if (fp) {
int arr[10];
size_t n = fread(arr, sizeof(int), 10, fp);
printf("读到 %zu 个整数\n", n);
fclose(fp);
}
return 0;
}feof(文件) —— 是否到文件末尾(到末尾返回非0)
#include <stdio.h>
int main() {
FILE* fp = fopen("data.bin", "wb");
if (fp) {
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
size_t n = fwrite(arr, sizeof(int), 10, fp);
printf("写入 %zu 个整数\n", n);
fclose(fp);
}
return 0;
}
#include <stdio.h>
int main() {
FILE* fp = fopen("data.bin", "wb");
if (fp) {
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
size_t n = fwrite(arr, sizeof(int), 10, fp);
printf("写入 %zu 个整数\n", n);
fclose(fp);
}
return 0;
}ferror(文件) —— 是否出错(出错返回非0)
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "r");
if (fp) {
char buf[256];
while (!feof(fp)) {
if (fgets(buf, 256, fp))
printf("%s", buf);
}
fclose(fp);
}
return 0;
}
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "r");
if (fp) {
char buf[256];
while (!feof(fp)) {
if (fgets(buf, 256, fp))
printf("%s", buf);
}
fclose(fp);
}
return 0;
}rewind(文件) —— 把文件指针移回开头
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "r");
if (fp) {
char buf[256];
fgets(buf, 256, fp);
if (ferror(fp)) printf("读取出错\n");
fclose(fp);
}
return 0;
}
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "r");
if (fp) {
char buf[256];
fgets(buf, 256, fp);
if (ferror(fp)) printf("读取出错\n");
fclose(fp);
}
return 0;
}fseek(文件, 偏移, 起点) —— 移动文件指针;起点:SEEK_SET/SEEK_CUR/SEEK_END
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "r");
if (fp) {
char buf[256];
fgets(buf, 256, fp);
rewind(fp);
fgets(buf, 256, fp);
fclose(fp);
}
return 0;
}
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "r");
if (fp) {
char buf[256];
fgets(buf, 256, fp);
rewind(fp); // 读完后回到开头再读一遍
fgets(buf, 256, fp);
fclose(fp);
}
return 0;
}字符串缓冲区操作
🎯 sprintf(缓冲区, 格式...) —— 格式化写入字符数组(不安全,可能溢出)
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "r");
if (fp) {
fseek(fp, 10, SEEK_SET);
char buf[256];
fgets(buf, 256, fp);
fclose(fp);
}
return 0;
}
#include <stdio.h>
int main() {
FILE* fp = fopen("data.txt", "r");
if (fp) {
fseek(fp, 10, SEEK_SET); // 从开头向后移10字节
char buf[256];
fgets(buf, 256, fp);
fclose(fp);
}
return 0;
}snprintf(缓冲区, 大小, 格式...) —— 安全版本,最多写 大小-1 个字符
#include <stdio.h>
int main() {
char buf[100];
sprintf(buf, "x=%d", 42);
printf("%s\n", buf);
return 0;
}
#include <stdio.h>
int main() {
char buf[100];
sprintf(buf, "x=%d", 42);
printf("%s\n", buf);
return 0;
}sscanf(字符串, 格式...) —— 从字符串格式化读取
#include <stdio.h>
int main() {
char buf[100];
snprintf(buf, sizeof(buf), "x=%d", 42);
printf("%s\n", buf);
return 0;
}
#include <stdio.h>
int main() {
char buf[100];
snprintf(buf, sizeof(buf), "x=%d", 42);
printf("%s\n", buf);
return 0;
}格式化说明符(printf / scanf 通用核心)
说明符 对应类型 示例
%d / %i int printf("%d", 42)
%ld long printf("%ld", 100000L)
%lld long long printf("%lld", 9999999999LL)
%u unsigned int printf("%u", 100u)
%f float/double printf("%f", 3.14)
%f double(scanf) scanf("%f", &x)
%e / %E 科学计数法 printf("%e", 1000.0) → 1.000000e+03
%g / %G 自动选格式 printf("%g", 3.14) → 3.14
%c char printf("%c", 'A')
%s char* 字符串 printf("%s", "hello")
%p 指针 printf("%p", ptr)
%% 百分号本身 printf("100%%") → 100%
%x / %X 十六进制 printf("%x", 255) → ff
%o 八进制 printf("%o", 8) → 10
进阶格式:%[宽度][.精度]说明符
printf("%5d", 42) → " 42"(占5位,右对齐)
printf("%-5d", 42) → "42 "(左对齐)
printf("%.2f", 3.14159) → "3.14"(保留2位小数)
printf("%05d", 42) → "00042"(补零)
#include <stdio.h>
int main() {
int a, b;
sscanf("10 20", "%d %d", &a, &b);
printf("a=%d, b=%d\n", a, b);
return 0;
}
#include <stdio.h>
int main() {
int a, b;
sscanf("10 20", "%d %d", &a, &b);
printf("a=%d, b=%d\n", a, b);
return 0;
}文件打开模式
"r" —— 只读(文件必须存在)
"w" —— 只写(文件不存在则创建,存在则清空!)
"a" —— 追加写(文件不存在则创建,存在则在末尾加)
"r+" —— 读写(文件必须存在)
"w+" —— 读写(创建或清空)
"a+" —— 读+追加写
加 'b' 表示二进制模式,如 "rb"、"wb"
⚠️ 1. scanf 的变量必须加 &(取地址),否则会崩溃。
2. 正确:scanf("%d", &x); 错误:scanf("%d", x); 2. printf 的格式化符和参数类型必须匹配,否则结果未定义。
3. 比如用 %d 输出 double 会得到乱码。
4. 3. sprintf 不检查缓冲区大小,容易溢出,推荐用 snprintf。
5. 4. fopen 可能失败(文件不存在、权限不足),一定要检查返回值 是否为 NULL,不要直接使用。
6. 5. 打开的文件必须 fclose,否则资源泄漏。
7. 6. C++ 中 <stdio.h> 和 <iostream> 可以混用,但默认是同步的, 混合大量输出时可能变慢。
8. 可用 ios::sync_with_stdio(false) 取消同步(但之后不要混用)。
💡 💡 竞赛中很多人喜欢用 printf/scanf,因为比 cin/cout 快、格式化方便。
💡 两者各有场景, 了解 cstdio 能看懂大量 C 语言代码和老项目。