C语言学习——文件

news2024/11/14 10:55:48

目录

十三、文件

13.1C文件概述

13.2文件类型指针

13.3文件的打开与关闭

文件的打开(fopen函数)

文件的关闭(fclose函数)

13.4文件的读写

fputc函数和fgetc函数(putc函数和getc函数)

fread函数和fwrite函数

fprintf函数和fscanf函数


十三、文件

13.1C文件概述

文件(file)是程序设计中一个重要的概念。所谓“文件”一般指存储在外部介质上数据的集合。一批数据是以文件的形式存放在外部介质(如磁盘)上的。操作系统是以文件为单位对数据进行管理的,也就是说,如果想找存在外部介质上的数据,必须先按文件名找到所指定的文件,然后再从该文件中读取数据。要向外部介质上存储数据也必须先建立一个文件(以文件名标识),才能向它输出数据。

前面我们所用到的输入和输出,都是以终端为对象的,即从终端键盘输入护具,运行结果输出在终端上。从操作系统的角度看,每一个与主机相联的输入输出设备都看作是一个文件。例如:终端键盘是输入文件,显示屏和打印机是输出文件。

在程序运行时,常常需要将一些数据(运行的最终结果或中间数据)输出到磁盘上存放起来,以后需要时再从磁盘中输入到计算机内存。这就要用到磁盘文件。

C语言把文件看作是一个字符(字节)的序列,即由一个一个字符(字节)的数据顺序组成的。根据数据的形式,可分为ASCII文件和二进制文件。ASCII文件又称文本(text)文件,它的每一个字节放一个ASCII代码,代表一个字符。二进制文件时把内存中的数据按其在内存中的存储形式原样输出到磁盘存放。如果有一个整数10 000 ,在内存中占2个字节,如果按ASCII码形式输出,则占5个字节,而按二进制形式输出,在磁盘上只占2个字节,见下图。用ASCII码形式输出与字符一一对应,一个字节代表一个字符,因而便于对字符进行逐个处理, 也便于输出字符。但一般占存储空间较多,而且要花费转换时间(二进制形式与ASCII码间的转换)。用二进制形式输出数值,可以节省外存空间和转换时间,但一个字节并不对应一个字符,不能直接输出字符形式。一般中间用结果数据需要暂时保存在外存上,以后又需要输入到内存中,常用二进制文件保存。

由前所述,一个C文件是一个字节流或二进制流。它把数据看作是一连串的字符(字节),而不考虑记录的界限。换句话说,C语言中文件并不是由记录(record)组成的(这是和PASCAL或其他高级语言不同的)。在C语言中对文件的存取是以字符(字节)为单位的。

输入输出的数据流的开始和结束仅受程序控制而不受物理符号(如回车换行符)控制。也就是说,在输出时不会自动增加回车换行符以作为记录的标志,输入时不以回车换行符作为记录的间隔(事实上C文件并不由记录构成)。把这种文件成为流式文件。C语言允许对文件存取一个字符,这就增加了处理的灵活性。

在过去使用的C版本(如UNIX系统下使用的C)有两种对文件的处理方法:一种叫“缓冲文件系统”,一种叫“非缓冲文件系统”。所谓缓冲文件系统是指系统自动地在内存区为每一个正在使用的文件开辟一个缓冲区。从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去。如果从磁盘向内存读入数据,则一次从磁盘文件将一批数据输入到内存缓冲区(充充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(给程序变量),见下图。缓冲区的大小由各个具体的C版本确定,一般为512字节。

所谓“非缓冲文件系统”是指系统不自动开辟确定大小的缓冲区,而由程序为每个文件设定缓冲区。

在UNIX系统下,用缓冲文件系统来处理文本文件,用非缓冲文件系统处理二进制文件。用缓冲文件系统进行的输入输出又称为高级(或高层)磁盘输入输出(高层I/O),用非缓冲文件系统进行的输人输出又称为低级(低层)输入输出系统。ANSI C标准不采用非缓冲文件系统,而只采用缓冲文件,即既用缓冲文件系统处理文本文件,也用它来处理二进制文件,也就是将缓冲文件系统扩充为可以处理二进制文件。

在C语言中,没有输入输出语句,对文件的读写都是用库函数来实现的。ANSI 规定了标准输入输出函数,用它们对文件进行读写。

13.2文件类型指针

缓冲文件系统中,关键的概念是“文件指针”。每个被使用的文件都在内存中开辟一个区,用来存放文件的有关信息(如文件的名字。文件状态及文件的当前位置等)。这些信息室保存在一个结构体变量中的。该结构体类型是由系统定义的,取名为FILE。Turbo C在stdio.h文件中有以下的文件类型声明。

typedef struct {
    short level;  //缓冲区“满”或“空”的程度
    unsigned flags; //文件状态标志
    char fd;  //文件描述符
    unsigned char hold;  //如无缓冲区不读取字符
    short bsize;  //缓冲区的大小
    unsigned char *buffer;  //数据缓冲区的位置
    unsigned ar *curp;  //指针,当前的指向
    unsigned istemp;  //临时文件,指示器
    short token;  //用于有效性检查
}FILE;

有了结构体FILE类型之后,可以用它来定义若干个FILE类型的变量,以变存放若干个文件的信息。例如,可以定义以下FILE类型的数组:

FILE f[5];  //定义了一个结构体数组f,它有5个元素,可以用来存放5个文件的信息

可以定义文件型指针变量。例如:

FILE *fp;  

fp是指向一个FILE类型结构体变量的指针变量。可以使fp指向某一文件的结构体变量,从而通过该结构体变量中的文件信息能够访问该文件。也就是说,通过文件指针变量能够找到与它相关的文件。如果有n个文件,一般应设n个指针变量(指向FILE类型结构体的指针变量),使它们分别指向n个文件(确切地说,指向存放该文件信息的结构体变量),以实现对文件的访问。

13.3文件的打开与关闭

和其他高级语言一样,对文件读写之前应该“打开”该文件,在使用结束之后应“关闭”该文件。

文件的打开(fopen函数)

调用方式为:

FILE *p;

fp = fopen(文件名,使用文件的方式);

例如:

fp = fopen("a1","r");

可以看出,在打开一个文件时,通知编译系统以下3个信息:

  • 需要打开的文件名,也就是准备访问的文件的名字

  • 使用文件的方式

  • 让哪一个指针变量指向被打开的文件

使用文件的方式见下表:

文件使用方式含义
"r"(只读)为输入打开一个文本文件
"w"(只写)为输出打开一个一个文本文件
"a"(追加)向文本文件尾添加数据
"rb"(只读)为输入打开一个二进制文件
"wb"(只写)为输出打开一个二进制文件
"ab"(追加)向二进制文件尾添加数据
"r+"(读写)为读写打开一个文本文件
"w+"(读写)为读写建立一个新的文本文件
"a+"(读写)为读写打开一个文本文件
"rb+"(读写)为读写打开一个二进制文件
"wb+"(读写)为读写建立一个新的二进制文件
"ab+"(读写)为读写打开一个二进制文件

说明:

(1)用"r"方式打开的文件只能用于向计算机输入而不能用作向该文件输出数据,而且该文件应该已经存在,不能用"r"方式打开一个并不存在的文件,即输入文件;否则出错。

(2)用"w"方式打开的文件只能用于向该文件写数据,即输出文本,而不能用来向计算机输入。如果原来不存在该文件,则在打开时新建立一个以指定名字命名的文件。如果原来已经存在一个以该文件命名的文件,则在打开时将该文件删去,然后重新建立该文件。

(3)如果希望向文本末尾添加新的数据(不希望删除原有数据),则应该用"a"的方式打开。但此时文件必须存在;否则将得到出错信息。打开时,位置指针移到文件末尾。

(4)用"r+"、"w+"、"a+"方式打开的文件既可以用来输入数据,也可以用来输出数据。用"r+"方式时该文件已经存在,以便能向计算机输入数据。用"w+"方式则新建一个文件,先向此文件写数据,然后可以读此文件中的数据。用"a+"方式打开的文件,原来的文件不被删去,位置指针移到文件末尾,可以添加,也可以读。

(5)如果文件不能实现打开的任务,fopen函数将会带回一个出错的信息。出错的原因可能是用"r"方式打开一个并不存在的文件;磁盘已满无法建立新文件等。此时fopen函数将带回一个空指针值NULL。

if ((fp=open("file1","r"))==NULL)
{
    printf("cannot open this file \n");
    exit(0);  //关闭所有文件,终止正在执行的程序,待用户检查出错误,修改后再进行
}

(6)以上方式可以打开文本文件或二进制文件,这是SNSI C的规定,用同一种缓冲文件系统来处理文本文件和二进制文件。

(7)在向计算机输入文本文件时,将回车换行符转换为一个换行符,在输出时把换行符换成为回车和换行两个字符。在用二进制文件时,不进行这种转换,在内存的数据形式与输出到外部文件中的数据形式完全一致,一一对应。

(8)在程序开始运行时,系统自动打开3个标准文件:标准输入、标准输出、标准出错输出。通常这3个文件都与终端相联系。

文件的关闭(fclose函数)

在使用完一个文件后应该关闭它,以防止它再被误用。“关闭”就是使文件指针变量不指向该文件。

fclose调用的一般形式为:

fclose(文件指针);

例如:

fclose(fp);

在程序终止前应该关闭所有文件,如果不关闭文件,则会丢失数据。

13.4文件的读写

fputc函数和fgetc函数(putc函数和getc函数)

fputc函数

把一个字符写到磁盘文件上去。其调用的一般形式为:

fputc(ch,fp);

fgetc函数

从指定的文件读入一个字符,该文件必须是以读或读写方式打开的。其调用的一般形式为:

ch = fgetc(fp);

fputc和fgetc函数使用举例

从键盘输入一些字符,逐个把它们送到磁盘上去,直到输入一个“#”为止。

#include<stdio.h>
#include<stdlib.h>
​
int main()
{
    FILE *fp;
    char ch,filename[10];
    scanf("%s",filename);
    if((fp=fopen(filename,"w"))==NULL)
    {
        printf("cannot open this file \n");
        exit(0); //终止程序 
    }
    ch = getchar();  //此语句用来接收在执行scanf语句时最后输入的回车符 
    ch = getchar();  //接收输入的第一个字符
    while(ch!='#')
    {
        fputc(ch,fp);
        putchar(ch);
        ch = getchar(); 
     } 
    putchar(10);  //向屏幕输出一个换行符 
    fclose(fp);  
    return 0;
}
​
//输入
file.c
computer and c#
//输出 
computer and c 

将一个磁盘文件中的信息复制到另一个磁盘文件中。

#include<stdio.h>
#include<stdlib.h>
​
int main()
{
    FILE *in,*out;
    char ch,infile[10],outfile[10];
    printf("Enter the infile name:\n");  //输入原有磁盘名 
    scanf("%s",infile);
    printf("Enter the outfile name:\n");  //输入新复制的磁盘文件名 
    scanf("%s",outfile);
    if((in=fopen(infile,"r"))==NULL)
    {
        printf("cannot open infile \n");
        exit(0); //终止程序 
    }
    if((out=fopen(outfile,"w"))==NULL)
    {
        printf("cannot open outfile \n");
        exit(0); //终止程序 
    }
    while(!feof(in))fputc(fgetc(in),out);
    fclose(in);
    fclose(out);
    return 0;
}

以上程序是按文本文件方式处理的。也可以用此程序来复制一个二进制文本,只需要将两个fopen函数中的"r"和"w"分别改成"rb"和"wb"即可。

fread函数和fwrite函数

用fgetc和fputc函数可以用来读写文件中的一个字符。但是常常要求一次读入一组数据(例如,一个实数或一个结构体变量的值),ANSI C标准提出设置两个函数(fread函数和fwrite函数),用来读写一个数据块。它们的一般调用形式为:

fread(buffer,size,count,fp);

fwrite(buffer,count,fp);

其中

buffer:是一个指针。对fread来说,它是读入数据的存放地址。对fwrite来说,是要输出数据的地址(以上指的是起始地址)。

size:要读写的字节数。

count:要进行读写多少个size字节的数据项。

fp:文件型指针。

如果文件以二进制形式打开,用fread和fwrite函数就可以读写任何类型的信息,例如:

fread(f,4,2,fp);

其中f是一个实型数组名。一个实型变量占4个字节。这个函数从fp所指向的文件读入2个4个字节的数据,存储到数组f中。

如果有一个如下的数据结构类型:

struct student_type{
    char name[10];
    int num;
    int age;
    char addr[30];
}stud[40];

结构体数组stud有40个元素,每一个元素用来存放一个学生的数据(包括姓名、学号、年龄、地址)。假设学生的数据已存放到磁盘文件中,可以用下面的for语句和fread函数读入40个学生的数据。

for(int i=0;i<40;i++)
{
    fread(&stud[i],sizeof(struct student_type),1,fp);
}

同样,以下for语句和fwrite函数可以将内存中的学生数据输出到磁盘文件中去:

for(int i=0;i<40;i++)
{
    fwrite(&stud[i],sizeof(struct student_type),1,fp);
}

如果fread或fwrite调用成功,则函数返回值为count的值,即输入或输出数据项的完整个数。

下面写一个完整的程序。

从键盘输入4个学生的有关数据,然后把它们转存到磁盘文件上去。

#include<stdio.h>
#define SIZE 4
​
struct student_type{
    char name[10];
    int num;
    int age;
    char addr[15];
}stud[SIZE];
​
void save()
{
    FILE *fp;
    int i;
    if((fp=fopen("stu_list","wb"))==NULL)
    {
        printf("cannot open file\n");
        return ;
    }
    for(i=0;i<SIZE;i++)
    {
        if(fwrite(&stud[i],sizeof(struct student_type),1,fp)!=1)
        {
            printf("file write error\n");
        }
    }
    fclose(fp);
 } 
 
int main()
{
    int i;
    for(i=0;i<SIZE;i++)
    {
        scanf("%s %d %d %s",&stud[i].name,&stud[i].num,&stud[i].age,&stud[i].addr);
    }
    save();
}
//输入:
Zhang 1001 19 room_101
Fun 1002 20 room_102
Tan 1003 21 room_103
King 1004 21 room_104

程序运行时,屏幕上无任何信息,只是将从键盘输入的数据送到磁盘上。为了验证在磁盘文件“stu_list”中是否存在此数据,可以用以下程序从“stu_list”文件中读入数据,然后在屏幕上输出。

#include<stdio.h>
#define SIZE 4
​
struct student_type{
    char name[10];
    int num;
    int age;
    char addr[15];
}stud[SIZE];
​
​
int main()
{
    int i;
    FILE *fp;
    fp = fopen("stu_list","rb");
    for(i=0;i<SIZE;i++)
    {
        fread(&stud[i],sizeof(struct student_type),1,fp);
        printf("%-10s%4d%4d%-15s\n",stud[i].name,stud[i].num,stud[i].age,stud[i].addr);
    }
    fclose(fp);
}
​
//输出:
Zhang     1001  19room_101
Fun       1002  20room_102
Tan       1003  21room_103
King      1004  21room_104 

请注意输入输出数据的情况。从键盘输入4个学生的数据是ASCII码,也就是文本文件。在送到计算机内存时,回车和换行符转换成一个换行符。再从内存以"wb"方式(二进制)输出到"stu_list"文件,此时不发生字符转化,按内存中存储形式原样输出到磁盘文件上。在上面的验证程序中,又用fread函数从"stu_list"文件向内存肚饿数据,注意此时用的是"rb"方式,即二进制方式,数据按原样输入,也不发生字符转换。也就是这时内存中的数据恢复到第一个程序向"stu_list"输出以前的情况。最后在验证程序中,用printf函数输出到屏幕,printf是格式输出函数,输出ASCII码,在屏幕上显示字符。换行符又转换为回车加换行符。

fprintf函数和fscanf函数

fprintf函数、fscanf函数与printf函数、scanf函数作用相仿,都是格式化读写函数。只有一点不同:fprintf函数和fscanf函数的读写对象不是终端,而是磁盘文件。它们的一般调用格式为:

fprintf(文件指针,格式字符串,输出表列);

fscanf(文件指针,格式字符串,输出表列);

例如:

fprintf(fp,"%d,%6.2f",i,t);
fscanf(fp,"%d %f",&i,&t);

用fprintf和fscanf函数对磁盘文件读写,使用方便,容易理解,但由于在输入时要将ASCII码转换为二进制形式,在输出时又要将二进制形式转换成字符,花费时间较多。因此,在内存与磁盘频繁交换数据的情况下,最好不用fprintf和fscanf函数,而用fread和fwrite函数。

声明:本文章为个人学习笔记,资料整理参考谭浩强《C程序设计(第三版)》如有错误,欢迎大家指正!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2067297.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在亚马逊云科技上通过LangChain ReAct Agent开发金融多模态数据AI分析中台

项目简介&#xff1a; 小李哥将继续每天介绍一个基于亚马逊云科技AWS云计算平台的全球前沿AI技术解决方案&#xff0c;帮助大家快速了解国际上最热门的云计算平台亚马逊云科技AWS AI最佳实践&#xff0c;并应用到自己的日常工作里。 本次介绍的是如何在亚马逊云科技机器学习托…

简易版营业厅宽带系统

TOC ssm018简易版营业厅宽带系统jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管…

音频Transformer架构

第3单元:音频Transformer架构 本课程中,我们主要关注Transformer模型以及它们如何应用于音频任务。虽然您不需要了解这些模型的内部细节,但了解使它们工作的主要概念很有用,因此我们在本小节中回顾一下关于Transformer的知识。有关transformer的深入了解,请查看我们的NLP…

互联网的发展是否加剧了数字鸿沟?

有人问&#xff1a;互联网的发展是否加剧了数字鸿沟。 互联网的发展确实在某种程度上加剧了数字鸿沟。虽然互联网的普及为全球范围内的人们提供了前所未有的访问信息、教育资源和经济机会的机会&#xff0c;但其发展也凸显并放大了不同群体之间的差距&#xff0c;比如以下几个…

dokcer 安装 redis(单机版)

准备工作 拉取redis镜像 docker pull redis 通过docker-compose 安装redis 很方便、很简单 先安装docker&#xff0c;参考我这个安装示例进行安装 https://blog.csdn.net/qq_33192671/article/details/13714973 然后安装docker-compose&#xff0c;要是拉取docker-compose无…

【在Linux世界中追寻伟大的One Piece】IO基础

目录 1 -> 回顾 1.1 -> 回顾C文件接口 1.2 -> 总结 2 -> 系统文件I/O 3 -> 接口介绍 3.1 -> open 3.2 -> open函数返回值 3.3 -> 文件描述符fd 4 -> 0 & 1 & 2 5 -> 文件描述符的分配规则 6 -> 重定向 7 -> 使用dup2系…

跨链互通:Web3如何实现多链互操作性

随着区块链技术的发展&#xff0c;各类区块链网络不断涌现&#xff0c;然而&#xff0c;不同链之间的互操作性问题成为了一个重要挑战。跨链互通&#xff08;Cross-chain Interoperability&#xff09;技术正是为了解决这一问题&#xff0c;旨在打破各区块链网络间的壁垒&#…

恒创科技:如何管理和减少Windows服务器 CPU 负载?

CPU 负载是衡量网络服务器或计算机中央处理器 (CPU) 在任意给定时间内处理工作量的指标。它通常表示 CPU 正在执行或排队等待处理的进程数。 如何读取和管理CPU负载&#xff1a; 对于 Windows 系统 Windows 本身不支持“top”和“ps”命令&#xff0c;而类 Unix 系统则支持。不…

Xinstall助力App运营,邀请码自动识别,效率翻倍!

在App推广和运营的道路上&#xff0c;邀请码一直是一个让人又爱又恨的存在。它能够帮助我们追踪用户来源&#xff0c;衡量推广效果&#xff0c;但同时&#xff0c;繁琐的填写步骤也让许多潜在用户望而却步。然而&#xff0c;随着Xinstall的出现&#xff0c;这一切都将迎来颠覆性…

Promise学习之同步与异步

目录 前言 一、同步与异步 (一) 同步 (二) 异步 二、总结 (一) 同步 (二) 异步 前言 Java有多线程&#xff0c;前端有同步与异步&#xff0c;异步操作可以优化用户体验、提高性能与响应、处理并发与并行任务等等&#xff0c;异步操作有发送Ajax请求、读文件等&#xff0…

简明的Arthas故障排查实践

写在文章开头 Arthas是一款强大的开源Java诊断程序,它可以非常方便的启动并以界面式的方式和Java程序进行交互,支持监控程序的内存使用情况、线程信息、gc情况、甚至可以反编译并修改现上代码等。所以它成为笔者进行线上问题排查的重要手段,而本文将从实际使用的角度介绍一下…

我带着我的未来回来了!

&#x1f51d;&#x1f51d;&#x1f51d;&#x1f51d;&#x1f51d;&#x1f51d;&#x1f51d;&#x1f51d;&#x1f51d;&#x1f51d;&#x1f51d;&#x1f51d;&#x1f51d;&#x1f51d;&#x1f51d; &#x1f947;博主昵称&#xff1a;小菜元 &#x1f35f;博客主页…

第九周:机器学习笔记

第九周机器学习周报 摘要Abstract机器学习——Spatial Transformer1.1 How to transform an image/feature map?&#xff08;怎么做&#xff09;1.2 Interpolation&#xff08;插值&#xff09;1.3 spatial Transformer的应用 Pytorch学习1. 线性层2. 其他层的介绍3. 搭建小实…

Leetcode 237.19.83.82 删除链表重复结点 C++实现

Leetcode 237. 删除链表中的节点 问题&#xff1a;有一个单链表的head&#xff0c;我们想删除它其中的一个节点node。给你一个需要删除的节点 node 。你将 无法访问 第一个节点head。链表的所有值都是唯一的&#xff0c;并且保证给定的节点 node不是链表中的最后一个节点。删除…

buuctf [ACTF新生赛2020]usualCrypt

前言&#xff1a;学习笔记。 常规&#xff1a; 下载 解压 查壳。 32位IDA pro打开 先查找字符串 在进入main() 分析&#xff1a; 关键函数&#xff1a; 第一部分&#xff1a; 大写转小写 小写转大写。 已知&#xff1a; 密文&#xff0c;以及加密过程由三部分组成。 那么逆向…

一款电容型、非接触式感知的智能水浸模组-WS11

水侵模组 - WS11&#xff08;Water Sensor-MC11S&#xff09;是一款电容型、非接触式感知的智能水浸模组&#xff0c;集成了高集成度差分式数字电容芯片MC11S。模组内嵌MCU&#xff0c;通过UART输出电容和检测状态信息&#xff0c;进行算法分析&#xff0c;有效滤除振动、凝露等…

Android自定义一个带背景的圆环形进度条(Kotlin)

前言 在Android开发过程中&#xff0c;难免遇到一些复杂的UI组件需要我们自定义 当然使用系统原生组件拼凑也能完成&#xff0c;但是UI复杂度增加了不说&#xff0c;在更新UI状态的时候还不好管理&#xff0c;最重要的是复用的价值不大&#xff0c;上述的操作很容易引增加码冗…

温湿度传感器---DHT11

温湿度传感器---DHT11 一、DHT11介绍二、DHT11原理图三、DHT11工作原理和时序3.1 DHT11工作时序 四、DHT11代码 一、DHT11介绍 DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器&#xff0c;传感器内部包括一个8位单片机控制一个电阻式感湿元件和一个NTC…

如何用Java SpringBoot+Vue打造“花开富贵”花园管理系统【实战教程】

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

学习【正点原子】新建寄存器版本MDK工程过程中错误总结

参考视频&#xff1a; 第21讲 基础篇-新建寄存器版本MDK工程2_哔哩哔哩_bilibili 参考文档&#xff1a;F:\STM32学习\【正点原子】探索者STM32F407开发板V3 资料盘(A盘)STM32F407 探索者开发指南V1.2 准备 使用的是正点原子探索者【STM32F407ZG】&#xff0c;编译软件版本是…