C语言 <stdio.h> 头文件详解

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)。
要在调试停止时自动关闭控制台,请启用"工具"->"选项"->"调试"->"调试停止时自动关闭控制台"。
按任意键关闭此窗口. . .
相关推荐
iCxhust2 小时前
C语言struct结构体实现类定义
c语言·开发语言·单片机·嵌入式硬件·51单片机
重生之我复员之后重当黄毛2 小时前
vscode配置c/c++环境
c语言·前端·visual studio
shangan_32 小时前
C语言学习
c语言·开发语言·学习
李永奉12 小时前
C语言-指针函数与函数指针及回调函数的使用
c语言·开发语言
杨丰玮41812 小时前
C语言基础知识(一)————各类符号的含义与输入输出库(stdio)
c语言·开发语言
水饺编程13 小时前
第1章,了解乌班图环境,使用浏览器和邮箱
c语言·汇编·ubuntu
Escalating_xu15 小时前
【C 语言】操作符详解(下):逗号、下标、结构体、类型转换与表达式求值
c语言
晓蛋16 小时前
C语言条件编译精讲
c语言·条件编译·宏定义·代码移植·预处理程序
ggaofeng17 小时前
Ctrl+C结束进程是如何实现的
服务器·c语言·网络