
C语言 stdio.h 头文件详解
- 一、头文件详解
-
- [1、 引言](#1、 引言)
- [2、为什么需要 <stdio.h>](#2、为什么需要 <stdio.h>)
- [3、 核心类型与宏定义](#3、 核心类型与宏定义)
-
- [3.1、 FILE 类型](#3.1、 FILE 类型)
- [3.2、 常用宏](#3.2、 常用宏)
- [4、 标准输入输出函数](#4、 标准输入输出函数)
-
- [4.1 printf ------ 格式化输出](#4.1 printf —— 格式化输出)
- [4.2、 scanf ------ 格式化输入](#4.2、 scanf —— 格式化输入)
- [4.3 、getchar 与 putchar ------ 字符输入输出](#4.3 、getchar 与 putchar —— 字符输入输出)
- [4.4 、gets 与 puts ------ 字符串输入输出](#4.4 、gets 与 puts —— 字符串输入输出)
- 5、文件操作函数
-
- [5.1 、fopen 与 fclose](#5.1 、fopen 与 fclose)
- [5.2、 fgetc 与 fputc ------ 文件字符读写](#5.2、 fgetc 与 fputc —— 文件字符读写)
- [5.3 、fgets 与 fputs ------ 文件字符串读写](#5.3 、fgets 与 fputs —— 文件字符串读写)
- [5.4 、fread 与 fwrite ------ 二进制读写](#5.4 、fread 与 fwrite —— 二进制读写)
- [5.5 、fseek 与 ftell ------ 文件定位](#5.5 、fseek 与 ftell —— 文件定位)
- [6、 格式化字符串函数](#6、 格式化字符串函数)
-
- [6.1、 sprintf ------ 格式化到字符串](#6.1、 sprintf —— 格式化到字符串)
- [6.2、 sscanf ------ 从字符串解析](#6.2、 sscanf —— 从字符串解析)
- [7、 错误处理与常见陷阱](#7、 错误处理与常见陷阱)
-
- [7.1、 检查返回值](#7.1、 检查返回值)
- [7.2 、缓冲区刷新](#7.2 、缓冲区刷新)
- [7.3、 避免使用 gets](#7.3、 避免使用 gets)
- [8、 总结](#8、 总结)
- 二、代码示例

一、头文件详解
1、 引言
在 C 语言的学习与开发中,<stdio.h> 是接触最早、使用频率最高的标准头文件之一。它的全称是 Standard Input Output Header,即标准输入输出头文件。无论是打印一行文字到屏幕,还是从键盘读取用户输入,都离不开这个头文件提供的函数与宏定义。
本文将带你系统梳理 <stdio.h> 中的核心内容,包括标准输入输出函数、文件操作函数、常用宏与类型定义,并通过可运行的代码示例帮助你在实际开发中熟练运用。
2、为什么需要 <stdio.h>
C 语言本身并不内置输入输出语句,输入输出功能是通过标准库函数实现的。<stdio.h> 正是这些标准输入输出函数的声明所在。当我们在代码中调用 printf、scanf、fopen 等函数时,编译器需要知道这些函数的原型(参数类型与返回值),否则会报隐式声明错误。
c
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
如果没有 #include <stdio.h>,上面的代码在编译时会提示 printf 未声明。因此,使用任何标准输入输出函数之前,都必须包含该头文件。
3、 核心类型与宏定义
<stdio.h> 中定义了一些重要的类型和宏,理解它们有助于阅读库函数的声明。
3.1、 FILE 类型
FILE 是文件流的结构体类型,用于表示一个打开的文件或设备。所有文件操作函数(如 fopen、fread、fclose)都通过 FILE * 指针来操作文件。
c
FILE *fp = fopen("test.txt", "r");
3.2、 常用宏
| 宏 | 含义 |
|---|---|
NULL |
空指针常量,通常为 0 |
EOF |
文件结束标志,值为 -1 |
stdin |
标准输入流(键盘) |
stdout |
标准输出流(屏幕) |
stderr |
标准错误流(屏幕) |
BUFSIZ |
缓冲区大小 |
SEEK_SET |
从文件开头定位 |
SEEK_CUR |
从当前位置定位 |
SEEK_END |
从文件末尾定位 |
4、 标准输入输出函数
4.1 printf ------ 格式化输出
printf 用于向标准输出(屏幕)打印格式化文本。
c
#include <stdio.h>
int main() {
int age = 25;
double height = 175.5;
char name[] = "Alice";
printf("姓名:%s\n", name);
printf("年龄:%d\n", age);
printf("身高:%.1f cm\n", height);
return 0;
}
常用格式说明符:
| 格式符 | 含义 |
|---|---|
%d |
十进制整数 |
%f |
浮点数 |
%c |
单个字符 |
%s |
字符串 |
%x |
十六进制整数 |
%p |
指针地址 |
4.2、 scanf ------ 格式化输入
scanf 从标准输入(键盘)读取格式化数据。注意:变量前必须加取地址符 &。
c
#include <stdio.h>
int main() {
int num;
printf("请输入一个整数:");
scanf("%d", &num);
printf("你输入的是:%d\n", num);
return 0;
}
4.3 、getchar 与 putchar ------ 字符输入输出
c
#include <stdio.h>
int main() {
char ch;
printf("请输入一个字符:");
ch = getchar();
printf("你输入的字符是:");
putchar(ch);
putchar('\n');
return 0;
}
4.4 、gets 与 puts ------ 字符串输入输出
⚠️ 注意:
gets不检查缓冲区边界,存在安全隐患,C11 标准已将其移除,推荐使用fgets替代。
c
#include <stdio.h>
int main() {
char str[100];
printf("请输入一行字符串:");
fgets(str, sizeof(str), stdin);
printf("你输入的是:");
puts(str);
return 0;
}
5、文件操作函数
<stdio.h> 提供了完整的文件读写能力,核心流程为:打开文件 → 读写文件 → 关闭文件。
5.1 、fopen 与 fclose
c
#include <stdio.h>
int main() {
FILE *fp = fopen("data.txt", "w");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
fprintf(fp, "Hello, File!\n");
fclose(fp);
return 0;
}
常用打开模式:
| 模式 | 含义 |
|---|---|
"r" |
只读,文件必须存在 |
"w" |
只写,文件不存在则创建,存在则清空 |
"a" |
追加写,文件不存在则创建 |
"r+" |
读写,文件必须存在 |
"w+" |
读写,文件不存在则创建,存在则清空 |
"rb" / "wb" |
二进制模式读写 |
5.2、 fgetc 与 fputc ------ 文件字符读写
c
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "w");
if (fp == NULL) return 1;
fputc('A', fp);
fputc('B', fp);
fclose(fp);
fp = fopen("test.txt", "r");
if (fp == NULL) return 1;
int ch;
while ((ch = fgetc(fp)) != EOF) {
putchar(ch);
}
putchar('\n');
fclose(fp);
return 0;
}
5.3 、fgets 与 fputs ------ 文件字符串读写
c
#include <stdio.h>
int main() {
FILE *fp = fopen("log.txt", "w");
if (fp == NULL) return 1;
fputs("第一行日志\n", fp);
fputs("第二行日志\n", fp);
fclose(fp);
fp = fopen("log.txt", "r");
if (fp == NULL) return 1;
char buffer[256];
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
printf("%s", buffer);
}
fclose(fp);
return 0;
}
5.4 、fread 与 fwrite ------ 二进制读写
c
#include <stdio.h>
typedef struct {
int id;
char name[20];
} Student;
int main() {
Student s1 = {1, "Tom"};
FILE *fp = fopen("student.dat", "wb");
if (fp == NULL) return 1;
fwrite(&s1, sizeof(Student), 1, fp);
fclose(fp);
Student s2;
fp = fopen("student.dat", "rb");
if (fp == NULL) return 1;
fread(&s2, sizeof(Student), 1, fp);
fclose(fp);
printf("学号:%d,姓名:%s\n", s2.id, s2.name);
return 0;
}
5.5 、fseek 与 ftell ------ 文件定位
c
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) return 1;
fseek(fp, 0, SEEK_END); // 跳到文件末尾
long size = ftell(fp); // 获取文件大小
printf("文件大小:%ld 字节\n", size);
fseek(fp, 0, SEEK_SET); // 回到文件开头
fclose(fp);
return 0;
}
6、 格式化字符串函数
6.1、 sprintf ------ 格式化到字符串
c
#include <stdio.h>
int main() {
char buffer[100];
int year = 2026;
sprintf(buffer, "今年是 %d 年", year);
printf("%s\n", buffer);
return 0;
}
6.2、 sscanf ------ 从字符串解析
c
#include <stdio.h>
int main() {
char data[] = "100 3.14";
int num;
double pi;
sscanf(data, "%d %lf", &num, &pi);
printf("整数:%d,浮点数:%.2f\n", num, pi);
return 0;
}
7、 错误处理与常见陷阱
7.1、 检查返回值
文件操作函数失败时通常返回 NULL 或 EOF,务必检查:
c
FILE *fp = fopen("nonexist.txt", "r");
if (fp == NULL) {
perror("打开文件失败");
return 1;
}
7.2 、缓冲区刷新
printf 输出到终端时通常是行缓冲,但输出到文件时是块缓冲。需要立即写入时使用 fflush:
c
printf("进度:50%%\n");
fflush(stdout);
7.3、 避免使用 gets
gets 无法限制输入长度,容易造成缓冲区溢出,务必使用 fgets 替代。
8、 总结
<stdio.h> 是 C 语言标准库中最基础也最重要的头文件,涵盖了:
- 标准输入输出 :
printf、scanf、getchar、putchar等; - 文件操作 :
fopen、fclose、fread、fwrite、fseek等; - 格式化处理 :
sprintf、sscanf等; - 常用宏与类型 :
FILE、EOF、NULL、stdin、stdout等。
掌握 <stdio.h> 的核心函数,是写出可交互、可持久化数据的 C 程序的基础。建议读者动手运行本文中的每个示例,并尝试修改参数观察输出变化,从而加深理解。
二、代码示例
c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <Windows.h>
int main(void)
{
SetConsoleOutputCP(CP_UTF8);
// ====================== 1. printf 格式化输出 ======================
printf("===== 1. printf 格式化输出 =====\n");
int i = 123;
double d = 3.1415926;
char ch = 'A';
char str[] = "hello stdio";
printf("整数 i = %d\n", i);
printf("带宽度:%6d\n", i); //占6字符宽度,右对齐
printf("补零:%06d\n", i);
printf("浮点数 d = %f\n", d);
printf("保留2位小数:%.2f\n", d);
printf("字符 ch = %c\n", ch);
printf("字符串 str = %s\n", str);
printf("十六进制 %x , %X\n", i, i);
// ====================== 2. scanf 标准输入 ======================
printf("\n===== 2. scanf 输入演示(可以注释掉,避免运行等待输入)=====\n");
int scan_num;
printf("请输入一个整数:");
scanf("%d", &scan_num);
printf("你输入的数字 = %d\n", scan_num);
// ====================== 3. putchar / getchar 单个字符IO ======================
printf("\n=====3. putchar / getchar =====\n");
putchar('X');
putchar('\n');
// ====================== 4. puts 输出字符串(自动追加换行) ======================
puts("\n=====4. puts 函数 =====");
puts("puts test string");
// ====================== 5. sprintf / sscanf 内存字符串读写(非常常用!) ======================
printf("\n=====5. sprintf / sscanf 内存字符串 =====\n");
char buf[64];
sprintf(buf, "num=%d, pi=%.2f", i, d);
printf("sprintf结果:%s\n", buf);
int parse_val;
sscanf("age:25", "age:%d", &parse_val);
printf("sscanf解析得到age = %d\n", parse_val);
// ====================== 6. 文件操作:fopen fclose fputs fgets ======================
printf("\n=====6 文件读写 fopen/fgets/fputs =====\n");
FILE* fp = fopen("test.txt", "w"); // w:写模式,清空原有内容
if (fp == NULL)
{
perror("fopen failed");
return -1;
}
fputs("第一行文本\n", fp);
fputs("第二行文本\n", fp);
fclose(fp);
// r 只读打开
fp = fopen("test.txt", "r");
if (fp == NULL)
{
perror("fopen read failed");
return -1;
}
char file_buf[128];
while (fgets(file_buf, sizeof(file_buf), fp) != NULL)
{
printf("读取文件行:%s", file_buf);
}
fclose(fp);
// ======================7 fwrite / fread 二进制读写 ======================
printf("\n=====7 fwrite fread 二进制读写 =====\n");
struct TestData
{
int a;
double b;
}w_data = { 100, 2.33 }, r_data;
fp = fopen("binary.dat", "wb"); //wb 二进制写
fwrite(&w_data, sizeof(struct TestData), 1, fp);
fclose(fp);
fp = fopen("binary.dat", "rb"); //rb 二进制读
fread(&r_data, sizeof(struct TestData), 1, fp);
fclose(fp);
printf("二进制读出 a=%d, b=%lf\n", r_data.a, r_data.b);
// ======================8 文件定位 fseek ftell rewind ======================
printf("\n=====8 fseek ftell rewind 文件指针定位 =====\n");
fp = fopen("test.txt", "r");
fseek(fp, 0, SEEK_END); //跳到文件末尾
long file_size = ftell(fp); //获取当前偏移,也就是文件大小
printf("test.txt 文件大小:%ld bytes\n", file_size);
rewind(fp); //文件指针回到开头
fclose(fp);
// ======================9 feof / ferror / perror / fflush ======================
printf("\n=====9 feof ferror fflush perror =====\n");
fp = fopen("test.txt", "r");
if (fp)
{
rewind(fp);
int ch_file;
while ((ch_file = fgetc(fp)) != EOF)
{
;
}
if (feof(fp))
{
printf("已经到达文件末尾 EOF\n");
}
if (ferror(fp))
{
perror("file error");
}
fflush(fp); //刷新缓冲区
fclose(fp);
}
// ======================10 文件删除、重命名 ======================
printf("\n=====10 remove 删除文件,rename重命名 =====\n");
rename("test.txt", "test_rename.txt");
remove("binary.dat");
printf("rename test.txt -> test_rename.txt , remove binary.dat\n");
printf("\n===== 程序结束 =====\n");
return 0;
}
运行结果:
c
===== 1. printf 格式化输出 =====
整数 i = 123
带宽度: 123
补零:000123
浮点数 d = 3.141593
保留2位小数:3.14
字符 ch = A
字符串 str = hello stdio
十六进制 7b , 7B
===== 2. scanf 输入演示(可以注释掉,避免运行等待输入)=====
请输入一个整数:123
你输入的数字 = 123
=====3. putchar / getchar =====
X
=====4. puts 函数 =====
puts test string
=====5. sprintf / sscanf 内存字符串 =====
sprintf结果:num=123, pi=3.14
sscanf解析得到age = 25
=====6 文件读写 fopen/fgets/fputs =====
读取文件行:第一行文本
读取文件行:第二行文本
=====7 fwrite fread 二进制读写 =====
二进制读出 a=100, b=2.330000
=====8 fseek ftell rewind 文件指针定位 =====
test.txt 文件大小:34 bytes
=====9 feof ferror fflush perror =====
已经到达文件末尾 EOF
=====10 remove 删除文件,rename重命名 =====
rename test.txt -> test_rename.txt , remove binary.dat
===== 程序结束 =====
d:\user\01417804\桌面\CppProject\cppTest\x64\Debug\cppTest.exe (进程 31456)已退出,代码为 0 (0x0)。
要在调试停止时自动关闭控制台,请启用"工具"->"选项"->"调试"->"调试停止时自动关闭控制台"。
按任意键关闭此窗口. . .

