《(本科)第9章 文件与编译预处理ppt课件.pptx》由会员分享,可在线阅读,更多相关《(本科)第9章 文件与编译预处理ppt课件.pptx(62页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、课程主讲人:第9章 文件与编译预处理C语言程序设计3C语言程序设计(第二版)4第9章 文件与编译预处理1本章通过一个简单的案例引入,主要介绍使用C语言进行文件管理的相关知识,重点介绍能够执行文件管理的函数。2同时也介绍宏定义、文件包含、条件编译这3种编译预处理在C语言程序中的作用。5第9章 文件与编译预处理知识点:文件指针文件的打开、关闭、读/写等常用操作文件内容的定位宏定义文件包含条件编译技能点:能熟练掌握Dev-C+集成环境的操作步骤6第9章 文件与编译预处理9.1 案 例 引 入【例9-1】读取一个文件的内容并显示出来。任务分析:要完成题目要求,需学习文件的打开,从文件中读取字符等知识点
2、。7第9章 文件与编译预处理9.2 文 件 概 述 一般来说,文件是有序数据的集合。程序文件是程序代码的有序集合,数据文件是一组数据的有序集合。文件是被存放在外部存储设备中的信息。对文件的处理过程就是面向文件的输入和输出过程。文件的输入过程是从文件中读出信息,文件的输出过程是往文件中写入信息,文件的输入过程使用读函数,实现文件输出的过程使用写函数。文件的读/写函数是实现文件操作的主要函数,本章将详细讲述文件的读/写函数。8第9章 文件与编译预处理 C语言文件被称为流式文件,其特点是不分记录或块,将文件看成是信息“流”或看成是一个字符流(文本文件),或看成是一个二进制流(二进制文件)。文件的存取
3、是以字符(字节)为单位的,读/写数据流的开始和结束受程序控制。任何一个文件都是以EOF结束,最简单的文件是只有结束符的空文件。9第9章 文件与编译预处理 C语言文件包含有设备文件和磁盘文件,例如,键盘是一种输入信息的文件,显示器屏幕和打印机是输出信息的文件,它们都属于设备文件。将内存的信息放到磁盘上保存,需要时再从磁盘上装入内存,这就要使用磁盘文件,磁盘文件是计算机中常用的文件。10第9章 文件与编译预处理 C语言文件按存放设备分为设备文件和磁盘文件;按数据的组织形式分为文本文件(ASCII码文件)和二进制文件。文本文件是按一个字节存放一个字符的ASCII码来存放的;二进制文件是按数据在内存中
4、的存储形式放到磁盘上的。例如,有一个整数10000,在内存中按二进制形式存放,占2个字节,将它放在磁盘上如按文本文件形式存放,占5个字节,每个数位占一个字节。两种存放方式各有利弊。以文本文件形式输出便于对字符进行处理,也便于输出字符,但是占用存储空间较多,并且要花费转换时间;以二进制文件形式输出可节省存储空间和转换时间,但是不能直接输出字符形式。11第9章 文件与编译预处理 9.3 文件的基本操作9.3.1 文件指针 文件指针是一种用来指向某个文件的指针。如果说某个文件指针指向某个文件,则是该文件指针指向某个文件存放在内存中的缓冲区的首地址。12第9章 文件与编译预处理每一个被使用的文件都要在
5、内存中开辟一个区域,用来存放与文件有关的信息,包括文件名字、文件状态和文件当前位置等。这些信息被保存在一个结构变量中,该结构变量所对应结构模式被系统定义为FILE,它被放在stdio.h文件中。有些版式的FILE被定义如下:type struct int fd; /文件号 int cleft /缓冲区内剩余的字符 int mode; /文件操作模式 char *mexic; /下一个字符位置 char *huff; /文件缓冲区位置 FILE;13第9章 文件与编译预处理 在文件操作的程序中,要使用FILE来定义文件指针,并且将打开的文件缓冲区的首地址赋给文件指针,让它指向该文件。例如:FIL
6、E *fp;其中,fp是一个指向文件的指针。fp = fopenabc.txr,r): 给fp赋值,使它指向abc.txt文件,于是fp便是一个指向abc.txt文件的指针。有了文件指针以后,对文件的操作(读、写和关闭等)都使用文件指针,而不使用文件名。14第9章 文件与编译预处理9.3.2 文件的打开文件的打开:fopen()。函数调用形式:FILE *fp;fp = fopen(文件名,文件使用方式);说明:(1)文件名和文件使用方式都是以字符串形式表示的。(2)若文件打开成功,则fopen()函数返回指向该文件信息结构体变量的指针;若打开失败,返回NULL。15第9章 文件与编译预处理文
7、件使用方式及其含义如表9-1所示。 表9-1 文件使用方式及其含义使 用 方 式含 义使 用 方 式含 义“r”(只读)为输入打开一个文本文件“r+”为读/写打开一个文本文件“rb”(只读)为输入打开一个二进制文件“rb+”为读/写打开一个二进制文件“w”(只写)为输出打开一个文本文件“w+”为读/写建立一个新的文本文件“wb”(只写)为输出打开一个二进制文件“wb+”为读/写建立一个新的二进制文件“a”(追加)向文本文件尾增加数据“a+”为读/写打开一个文本文件“ab”(追加)向二进制文件尾增加数据“ab+”为读/写打开一个二进制文件16第9章 文件与编译预处理注意:(1)要理解读和写的含义
8、,“读”是输入内存空间,“写”是输出外存磁盘。(2)对于打开方式,一旦选择就不能改变,除非关闭后重新打开。17第9章 文件与编译预处理9.3.3 文件的关闭 文件的关闭:fclose()。函数调用形式:fclose(文件指针);说明: (1)使文件指针变量不指向该文件,也就是文件指针变量与文件“脱钩”,此后不能再通过该指针对原来与其相联系的文件进行读/写操作。 (2)若关闭成功返回值为0;否则返回EOF(-1)。判断文件结束的函数:feof()。18第9章 文件与编译预处理调用形式:feof(文件指针);说明:feof()函数用来判断文件是否结束。如果遇到文件结束,函数返回非零值,否则返回0。
9、19第9章 文件与编译预处理 9.4 文件的读/写针对文本文件和二进制文件的不同性质,采用不同的读/写方式。对于文本文件,可按字符或字符串读/写;而对于二进制文件,可进行成块的读/写或格式化的读/写操作。对文件输入/输出操作包含3种类型:(1)对字符的输入/输出:调用getc()(或fgetc())、putc()(或fputc())函数。(2)对字符串的输入/输出:调用fgets()、fputs()函数。(3)对格式化的输入/输出:调用fscanf()、fprintf()函数。20第9章 文件与编译预处理 9.4.1 字符读/写函数字符读/写函数(fgetc()和fputc())用于输入和输出
10、一个字符。1输出一个字符到文件可以用putc()或fputc()函数输出一个字符到文件,其调用形式为:putc(ch,fp); 或 fputc(ch,fp);说明:(1)ch是待输出的某个字符,fp是文件指针。(2)函数的返回值是所输入的字符,出错时返回EOF。21第9章 文件与编译预处理 2从文件读取一个字符可以用getc()或fgetc()函数从文件读取一个字符,其调用形式为:ch = getc(fp); 或 ch = fgetc(fp); 说明:(1)函数的功能是从指定的文件中读入一个字符,并把它作为函数值返回。(2)函数从输入的当前位置返回一个字符,并将文件指针移到下一个字符处。若到文
11、件尾,则返回EOF。22第9章 文件与编译预处理 9.4.2 格式化读/写函数格式化读/写函数(fscanf()和fprintf())用于文件的格式化输入和输出。1格式化输入fscanf()函数输入的对象是磁盘上的文本文件中的数据,其调用形式为:fscanf(文件指针,格式控制字符串,输入项表);注意:文件中的两个整数之间用空格符(或跳格符、回车符)隔开。23第9章 文件与编译预处理 2格式化输出fprintf()函数按格式将内存中的数据转换成对应的字符,并以ASCII码形式输出到文本文件中。其调用格式为:fprintf(文件指针,格式控制字符串,输出项表);注意:文件中的两个整数之间用空格符
12、隔开。24第9章 文件与编译预处理9.4.3 数据块读/写函数数据块读/写函数(fread()和fwrite())用于按“记录”方式输入和输出。fread()函数和fwrite()函数分别用来读、写二进制文件。它们的调用形式为: fread(buffer,size,count,fp); fwerte(buffer,size,count,fp); 25第9章 文件与编译预处理说明:(1)buffer:是一个指针,对fread 来说,它是读入数据的存放地址;对fwrite来说,是要输出数据的地址(均指起始地址)。size:要读/写的字节数。count:要进行读/写多少个size字节的数据项。(2)
13、fread()和fwrite()函数以二进制方式读/写数据。调用成功后,返回count值;否则返回0。26第9章 文件与编译预处理 9.4.4 字符串读/写函数字符串读/写函数(fgets()和fputs())用于输入和输出一个字符串。1输出一个字符串到文件可用fputs()函数输出一个字符串到文件,其调用形式为:fputs(str,fp);说明:(1)str是待输出的字符串,可以是字符串常量、指针或字符数组名等。(2)输出时,字符串最后的“0”并不输出,也不自动加“n”。(3)输出成功,函数返回正整数,否则返回-1(EOF)。27第9章 文件与编译预处理2从文件中读入一个字符串可用fgets
14、()函数从文件中读入一个字符串,其调用形式为:fgets(str,n,fp)说明:(1)str是内存中存放字符串的起始地址,n是int型变量。(2)函数功能是从文件中读入n-1个字符放入以str为起始的内存中。(3)读取字符时直到遇到回车符、EOF或已读入所限定的字符数为止。28第9章 文件与编译预处理9.5 文件的定位“文件位置指针”表示当前读/写的数据在文件中的位置。1fseek()函数fseek()函数(一般用于二进制文件)用来移动文件位置指针到指定的位置上,接着执行的读/写操作将从此位置开始。其调用形式为:fseek(fp,offset,origin);29第9章 文件与编译预处理说明
15、:(1)offset是以字节为单位的位移量,是长整型。(2)origin是起始点,用来指定位移量是以哪个位置为基准,起始点可以用标识符或数字表示,如表9-2所示。30第9章 文件与编译预处理标 识 符数 字代表的起始点SEEK_SET0文件的开始SEEK_END2文件的结尾SEEK_CUR1文件当前位置表9-2 起始点表示31第9章 文件与编译预处理 2ftell()函数ftell()函数用来获得文件当前位置指针的位置,函数给出当前位置指针相对于文件开头的字节数。若fp指向一个已正确打开的文件,则函数的调用形式为:long t;t = ftell(fp);ftell()函数调用成功后,返回当前
16、位置指针相对文件开头的字节数;否则,返回-1L。32第9章 文件与编译预处理3rewind()函数rewind()函数用来使文件的位置指针回到文件的开头。其调用形式为:rewind(pf); 该函数没有返回值。33第9章 文件与编译预处理 9.6 编译预处理 编译预处理是指,在对源程序进行编译之前,先对源程序中的编译预处理命令进行处理;然后再将处理的结果和源程序一起进行编译,以得到目标代码。34第9章 文件与编译预处理 9.6.1 宏定义在语言中,“宏”分为无参数的宏(简称无参宏)和有参数的宏(简称有参宏)两种。 1无参宏定义 1)无参宏定义的一般格式#define 标识符 字符串其中:def
17、ine为宏定义命令;“标识符”为所定义的宏名,通常用大写字母表示,以便与变量区别;“字符串”可以是常数、表达式、格式串等。35第9章 文件与编译预处理2)使用宏定义的优点(1)可提高源程序的可维护性。(2)可提高源程序的可移植性。(3)减少源程序中重复书写字符串的工作量。36第9章 文件与编译预处理【例9-2】输入圆的半径,求圆的周长、面积和球的体积。要求使用无参宏定义圆周率。说明:(1)宏名一般用大写字母表示,以示与变量相区别,但这并非是规定。(2)宏定义不是语句,所以不能在行尾加分号。否则,宏展开时,会将分号作为字符串的1个字符,用于替换宏名。37第9章 文件与编译预处理(3)在宏展开时,
18、预处理程序仅以按宏定义简单替换宏名,而不作任何检查。如果有错误,只能由编译程序在编译宏展开后的源程序时发现。(4)宏定义命令#define出现在函数的外部,宏名的有效范围是:从定义命令之后,到本文件结束。通常,宏定义命令放在文件开头处。(5)在进行宏定义时,可以引用已定义的宏名。(6)对双引号括起来的字符串内的字符,即使与宏名同名,也不进行宏展开。38第9章 文件与编译预处理2符号常量在定义无参宏时,如果“字符串”是一个常量,则相应的“宏名”就是一个符号常量。恰当命名的符号常量,除具有宏定义的上述优点外,还能表达出它所代表常量的实际含义,从而增强程序的可读性。#define EOF -1 /文
19、件尾#define NULL 0 /空指针#define MIN 1 /极小值#define MAX 31 /极大值#define STEP 2 /步长39第9章 文件与编译预处理3有参宏定义1)带参宏定义的一般格式#define 宏名(形参表) 字符串2)带参宏的调用和宏展开(1)调用格式:宏名(实参表)。(2)宏展开:用宏调用提供的实参字符串,直接置换宏定义命令行中、相应形参字符串,非形参字符保持不变。40第9章 文件与编译预处理【例9-3】定义一个带有三个参数的宏MAX,求三个参数中的最大值。41第9章 文件与编译预处理3)说明(1)定义有参宏时,宏名与左圆括号之间不能留有空格。否则,编
20、译系统将空格以后的所有字符均作为替代字符串,而将该宏视为无参宏。(2)有参宏的展开,只是将实参作为字符串,简单地置换形参字符串,而不做任何语法检查。在定义有参宏时,在所有形参外和整个字符串外,均加一对圆括号。42第9章 文件与编译预处理(3)虽然有参宏与有参函数确实有相似之处,但不同之处更多,主要有以下几个方面: 调用有参函数时,是先求出实参的值,然后再复制一份给形参。而展开有参宏时,只是将实参简单地置换形参。 在有参函数中,形参是有类型的,所以要求实参的类型与其一致;而在有参宏中,形参是没有类型信息的,因此用于置换的实参,什么类型都可以。有时,可利用有参宏的这一特性,实现通用函数功能。43第
21、9章 文件与编译预处理 使用有参函数,无论调用多少次,都不会使目标程序变长,但每次调用都要占用系统时间进行调用现场保护和现场恢复;而使用有参宏,由于宏展开是在编译时进行的,所以不占运行时间,但是每引用一次,都会使目标程序增大一次。44第9章 文件与编译预处理4终止宏定义可以用#undef终止宏定义的作用域,一般形式为:#undef 宏名例如:#define PI 3.14int main(void) #undef PI 以上PI的作用域从#define PI 3.14命令行开始,到#undef PI命令行结束;从#undef以后PI变成无定义,不再代表3.14了。45第9章 文件与编译预处理9
22、.6.2 文件包含1文件包含的概念文件包含是指一个源文件可以将另一个源文件的全部内容包含进来。2文件包含处理命令的格式include 包含文件名 或 include 46第9章 文件与编译预处理两种格式的区别仅在于: (1)使用双引号:系统首先到当前目录下查找被包含文件,如果没找到,再到系统指定的“包含文件目录”(由用户在配置环境时设置)去查找。(2)使用尖括号:直接到系统指定的“包含文件目录”去查找。一般地说,使用双引号比较保险。47第9章 文件与编译预处理3文件包含的优点一个大程序,通常分为多个模块,并由多个程序员分别编程。有了文件包含处理功能,就可以将多个模块共用的数据(如符号常量和数据
23、结构)或函数,集中到一个单独的文件中。这样,凡是要使用其中数据或调用其中函数的程序员,只要使用文件包含处理功能,将所需文件包含进来即可,不必再重复定义它们,从而减少重复劳动。48第9章 文件与编译预处理4说明(1)编译预处理时,预处理程序将查找指定的被包含文件,并将其复制到#include命令出现的位置上。(2)常用在文件头部的被包含文件,称为“标题文件”或“头部文件”,常以“h”(head)作为扩展名,简称头文件。在头文件中,除可包含宏定义外,还可包含外部变量定义、结构类型定义等。49第9章 文件与编译预处理(3)一条包含命令,只能指定一个被包含文件。如果要包含n个文件,则要用n条包含命令。
24、(4)文件包含可以嵌套,即被包含文件中又包含另一个文件50第9章 文件与编译预处理【例9-4】文件包含使用实例。51第9章 文件与编译预处理9.6.3 条件编译一般情况下,源程序中的所有行都要参加编译,但特殊情况下可能需要根据不同的条件编译源程序中的不同部分。也就是说,源程序中的一部分内容只有满足一定条件才进行编译,或者,当条件成立时去编译一组语句,而当条件不成立时编译另一组语句,这就是“条件编译”。52第9章 文件与编译预处理条件编译命令有以下几种形式。1形式1#ifdef 标识符 程序段1#else 程序段2#endif功能:当指定的标识符在此之前已经被#define语句定义过,“程序段1
25、”被编译,否则,“程序段2”被编译。其中“#else”部分可以省略,即:#ifdef 标识符 程序段l#endif53第9章 文件与编译预处理例如,在调试程序时,经常要输出一些所需的信息,一旦调试结束,这些信息就不再需要了。一种处理方法可以采用条件编译来完成,只需在源程序中插入类似于以下的语句:#ifdef DEBUG printf(a = %d, b = %dn,a,b);#endif只要在程序的起始处有以下命令行:#define DEBUG程序在运行中就会输出a、b的值,以便调试程序时分析。54第9章 文件与编译预处理只要程序中需要输出的地方,都可以使用相同的方法来输出所需的信息。调试结束
26、后,只要将#define DEBUG语句删除即可,而不必去一一删除相关的printf语句,这样可以简化调试工作。2形式2#ifndef标识符程序段l #else程序段2 #endif功能:当指定的标识符在此之前没有被#deifne语句定义过,“程序段1”被编译,否则,“程序段2”被编译。类似于#ifdef,“#else”部分可以省略。55第9章 文件与编译预处理3形式3#if 表达式 程序段1#else 程序段2#endif功能:如果指定的表达式的值为真(非0),则编译程序段1,否则编译程序段2。56第9章 文件与编译预处理 【例9-5】条件编译实例。程序代码如下:每次要输出调试信息前,只需要
27、#ifdef DEBUG判断一次,运行结果如图9-5(a)所示;不需要了就在文件开始将#define DEBUG 注释掉,运行结果如图9-5(b)所示。57第9章 文件与编译预处理 9.7 程序设计举例【例9-6】设当前路径下有一个名为change.txt文件,现在将文件中所有小写字母改写为大写字母后存回原文件中,文件中其他字符不变,并将转换前跟转换后的文件内容显示出来。58第9章 文件与编译预处理 【例9-7】程序实现人员登录,即每当从键盘接收一个姓名,便在文件member.dat中进行查找,若此姓名已存在,则显示相应信息。若文件中没有该姓名,则将其存入文件member.dat中。当输入姓名
28、按【Enter】键或处理过程中出现错误时程序结束。59第9章 文件与编译预处理 【例9-8】一个程序写在多个源文件中的应用举例。程序包括三个文件,分别为9-8.c、9-8_1.c和9-8_2.c。在9-8.c程序中包含了9-8_1.c和9-8_2.c。程序代码如下:在文件9-8.c中,文件包含预编译命令:#include9-8_1.c#include9-8_2.c60第9章 文件与编译预处理将其他两个文件中的内容插入到文件包含命令所在位置,经过编译预处理后,源文件9-8.c包含的内容如下: /* 文件stdio.h 所包含的内容 */int fun1(int a,int b) /* 文件9-8
29、_1.c所包含的内容 */ return a+b;int fun2(int a,int b) /* 文件9-8_2.c所包含的内容 */61第9章 文件与编译预处理 return a*b;int main(void) printf(%dn,fun1(3,5); printf(%dn,fun2(3,5); return 0;程序说明:经过编译预处理后,在对文件9-8.c的编译过程中,C所处理的是一个完整的程序。62第9章 文件与编译预处理本章主要介绍了C语言中的文件。文件分为二进制文件和文本文件,用文件指针标识文件,当一个文件被打开时,可取得该文件指针。文件在读/写之前必须打开,读/写结束必须关闭。文件可按字符、字符串、数据块为单位进行读/写,也可按指定的格式进行读/写。文件内部的位置指示器指示当前的读/写位置,通过定位函数可以对文件实现随机读/写。同时也介绍了编译预处理,该功能是C语言特有的功能,它是在对源程序正式编译前由预处理程序完成的。宏定义是用一个标识符来表示一个字符串,这个字符串可以是常量、变量或表达式。在宏调用中将用该字符串代换宏名。宏定义可以带有参数,宏调用时是以实参代换形参。文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件。条件编译允许只编译源程序中满足条件的程序段,方便跨平台程序开发、编译、调试等。小 结