版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
了解磁盤文件的概念和用途掌握文件指針的概念和文件指針變量的定義方法深刻理解文件的讀、寫、定位等基本操作的實現(xiàn)熟悉文件的打開、關(guān)閉、讀、寫、定位等函數(shù)的調(diào)用形式掌握文件操作在程序設計中的應用方法掌握編譯預處理的基本概念和使用形式11.1文件概述【問題】數(shù)據(jù)在計算機中如何被保存和閱讀?11.1.1文件的概念所謂“文件”是指一組相關(guān)數(shù)據(jù)的有序集合。這個數(shù)據(jù)集有一個名稱,叫做文件名。例如:k:\24000101\program.c,其中k:\24000101就是路徑,program.c是文件名。當需要使用文件的時候,需要將文件調(diào)入內(nèi)存中。11.1.2文件的分類從用戶使用的角度看,文件可分為普通文件和設備文件從文件編碼和數(shù)據(jù)的組織方式來看,文件可分為ASCII碼文件和二進制碼文件。ASCII文件也稱為文本文件,文件在磁盤中存放時每個字符占一個字節(jié),每個字節(jié)中存放相應字符的ASCII碼。內(nèi)存中的數(shù)據(jù)存儲時需要轉(zhuǎn)換為ASCII碼。二進制文件則不同,內(nèi)存中的數(shù)據(jù)存儲的時候不需要進行數(shù)據(jù)轉(zhuǎn)換,存儲介質(zhì)上保存的數(shù)據(jù)采用與內(nèi)存數(shù)據(jù)一致的表示形式存儲。11.1.2文件的分類從C語言對文件的處理方法來看。舊的C版本(如Unix系統(tǒng)下使用的C)有兩種對文件的處理方法:一種叫“緩沖文件系統(tǒng)”,一種叫“非緩沖文件系統(tǒng)”。緩沖文件系統(tǒng):系統(tǒng)自動地在內(nèi)存區(qū)為每一個正在使用的文件名開辟一個緩沖區(qū)。從內(nèi)存向磁盤輸出數(shù)據(jù)必須先送到內(nèi)存中的緩沖區(qū),裝滿緩沖區(qū)后才一起送到磁盤去。非緩沖文件系統(tǒng):指系統(tǒng)不自動開辟確定大小的緩沖區(qū),而由程序為每個文件設定緩沖區(qū)。11.2文件操作11.2.1FILE文件類型指針
typedefstruct{shortlevel;/*緩沖區(qū)“滿”或“空”的程度*/unsignedflags;/*文件狀態(tài)標志*/charfd;/*文件描述符*/unsignedcharhold;/*無緩沖區(qū)不讀取字符*/shortbsize;/*緩沖區(qū)大小*/unsignedchar*buffer;/*數(shù)據(jù)緩沖區(qū)位置指針*/unsignedchar*curp;/*當前指針指向*/unsignedistemp;/*臨時文件指示器*/shorttoken;/*用于有效性檢查*/}FILE;
有了FILE類型以后可以定義文件類型指針變量。例如:
FILE*fp;標準設備文件C語言中的是由系統(tǒng)控制的,由系統(tǒng)自動打開和關(guān)閉,其文件結(jié)構(gòu)指針由系統(tǒng)命名,用戶無需說明即可直接使用,例如:stdin
標準輸入文件(鍵盤)stdout
標準輸出文件(顯示器)stderr
標準錯誤輸出文件(顯示器)11.2.2文件的打開操作C語言用fopen()函數(shù)來實現(xiàn)文件的打開。fopen函數(shù)的調(diào)用方式一般為:FILE*fp;fp=fopen(文件名,文件使用方式);例如:fp=fopen("result.txt","r");11.2.2文件的打開操作打開文件方式文件使用方式含義"r"只讀,文本以只讀方式打開一個已有的文本文件。"w"只寫,文本以只寫方式建立一個新的文本文件。如果該文件已存在則將它刪去,然后重新建立一個新文件。"a"追加,文本以添加方式打開一個文本文件,在文件末尾添加。如果該文件不存在,則建立一個新文件后再添加。"rb"只讀,二進制以只讀方式打開一個已有的二進制文件。"wb"只寫,二進制以只寫方式打開一個二進制文件。"ab"追加,二進制以添加方式打開一個二進制文件。11.2.2文件的打開操作文件使用方式含義"r+"讀寫,文本以讀寫方式打開一個已有的文本文件。"w+"讀寫,文本以讀寫方式建立一個新的文本文件。"a+"讀寫,文本以讀寫方式打開一個文本文件,在文件末尾添加和修改,如果文件不存在,則建立一個新文件后再添加和修改。"rb+"讀寫,二進制以讀寫方式打開一個已有的二進制文件。"wb+"讀寫,二進制以讀寫方式建立一個新的二進制文件。"ab+"讀寫,二進制以讀寫方式打開一個二進制文件。打開文件方式說明用以上方式可以打開文本文件或二進制文件,這是ANSIC的規(guī)定,即用同一種緩沖文件系統(tǒng)來處理文本文件和二進制文件。但目前使用的有些C編譯系統(tǒng)可能不完全提供所有這些功能(例如有的只能用“r”、“w”、“a”方式),有的C版本不用“r+”、“w+”、“a+”而用“rw”、“wr”、“ar”等,請讀者注意所用系統(tǒng)的規(guī)定。說明如果不能實現(xiàn)“打開”的任務,fopen函數(shù)將會返回一個出錯信息。出錯的原因可能是:用“r”方式打開一個并不存在的文件;磁盤出故障;磁盤已滿無法建立新文件等。此時fopen函數(shù)將帶回一個空指針值NULL(NULL在stdio.h文件中已被定義為0)。常用下面的方法打開一個文件:if((fp=fopen("filename","r“))==NULL)
{
printf("cannotopenthisfile.\n“);
exit(0);
}說明用“w”方式打開文件時,只能從內(nèi)存向該文件輸出(寫)數(shù)據(jù),而不能從文件向內(nèi)存輸入數(shù)據(jù)。如果該文件原來不存在,則打開時按指定文件名建立一個新文件。如果原來的文件已經(jīng)存在,則打開時將文件刪空,然后重新建立一個新文件,所以務必小心。說明用“a”方式打開文件時,向文件的尾部添加新數(shù)據(jù),文件中原來的數(shù)據(jù)保留,但要求文件必須存在,否則會返回出錯信息。打開文件時,文件的位置指針在文件末尾。用“r+”、“w+”、“a+”方式打開文件時,既可以輸入也可以輸出,不過三種方式是有區(qū)別的:“r+”方式要求必須文件存在;“w+”方式則建立新文件后進行讀寫;“a+”方式則保留文件原有的數(shù)據(jù),進行追加或讀的操作。說明在用文本文件向計算機輸入時,應將回車和換行兩個字符轉(zhuǎn)換為一個換行符;在輸出時,應將換行符轉(zhuǎn)換為回車和換行兩個字符。在用二進制文件時,不需進行這種轉(zhuǎn)換,因為在內(nèi)存中的數(shù)據(jù)形式與輸出到外部文件中的數(shù)據(jù)形式完全一致,一一對應。說明在程序開始運行時,系統(tǒng)自動打開三個標準文件:標準輸入、標準輸出、標準出錯輸出。通常這三個文件都與終端相聯(lián)系。因此以前我們所用到的從終端輸入或輸出,都不需要打開終端文件。系統(tǒng)自動定義了三個文件指針stdin、stdout和stderr,分別指向終端輸入、終端輸出和標準出錯輸出(也從終端輸出)。如果程序中指定要從stdin所指的文件輸入數(shù)據(jù),就是指從終端鍵盤輸入數(shù)據(jù)。11.2.3文件的關(guān)閉操作文件在使用完后應該及時關(guān)閉它,以防止它再被誤用。“關(guān)閉”就是釋放文件指針。釋放后的文件指針變量不再指向該文件,為自由的文件指針。這種方式可以避免文件中的數(shù)據(jù)丟失。釋放指針后不能再通過該指針對原對應的文件進行讀寫操作,除非再次用該指針變量打開該文件。用fclose函數(shù)關(guān)閉文件。fclose函數(shù)調(diào)用的一般形式為:fclose(文件指針);例如:fclose(fp);如果文件關(guān)閉成功,fclose函數(shù)返回值為0;如果關(guān)閉出錯,則返回值為EOF(-1)。這可以用ferror函數(shù)來測試。11.2.4文件的讀寫操作字符讀寫函數(shù)字符輸入函數(shù)fgetc從指定文件讀入一個字符,該文件必須是以讀或讀寫方式打開的。fgetc函數(shù)的調(diào)用形式為:ch=fgetc(fp);【例11-1】顯示文本文件readme.txt的內(nèi)容。
#include<stdio.h>voidmain(){FILE*fp; charch;if((fp=fopen("readme.txt","r"))==NULL) { printf("fileopenerror.\n"); exit(0); }while((ch=fgetc(fp))!=EOF)putchar(ch);fclose(fp);}該程序完成:從一文件名為“readme.txt”的磁盤文件中順序讀取字符,并在標準輸出設備顯示器上輸出。注意EOF為文本文件的結(jié)束標志。二進制文件中的數(shù)據(jù),某一個字節(jié)的值可能是-1,而這又恰好是EOF的值。所以,上述程序只適合處理文本文件。ANSIC已允許用緩沖區(qū)文件系統(tǒng)處理二進制文件.為了解決上述問題,ANSIC提供了一個feof函數(shù)來判斷文件是否真的結(jié)束。feof(fp)用來測試fp所指向的文件當前狀態(tài)是否為“文件結(jié)束”。如果是文件結(jié)束,函數(shù)feof(fp)的值為1(真),否則為0(假)。
注意如果想順序讀取一個二進制文件的數(shù)據(jù),上面的程序修改為:ch=fgetc(fp);while(!feof(fp))/*相當于while(feof(fp)==0)*/{putchar(ch);ch=fgetc(fp);}
字符輸出函數(shù)fputcfputc函數(shù)把一個字符輸出到磁盤文件上。其一般形式為:fputc(ch,fp);【說明】ch是要輸出的字符,它可以是一個字符常量,也可以是一個字符變量?!纠?1-2】從鍵盤上輸入的字符代碼順序存入名為“result.txt”的磁盤文件中,當鍵盤輸入Ctrl+Z時關(guān)閉文件,輸入結(jié)束。
#include"stdio.h"voidmain(){FILE*fp; intch;
if((fp=fopen("result.txt","w"))==NULL){printf("filecreatederror.\n");exit(0);}do{ch=getchar();/*注意次序,先輸入字符再寫到文件中*/fputc(ch,fp);}while(ch!=EOF);fclose(fp);}【例11-3】編程完成將文本文件readme.txt復制到result.txt中。while((ch=fgetc(fp1))!=EOF)
fputc(ch,fp2);
fclose(fp1);
fclose(fp2);
}#include<stdio.h>
main()
{
FILE*fp1,*fp2;
charch;
if((fp1=fopen("readme.txt","r"))==NULL)
{
printf("file1opennederror.\n");
exit(0);
}
if((fp2=fopen("result.txt","w"))==NULL)
{
printf("file2createderror.\n");
exit(0);
}字符串讀寫函數(shù)讀文件字符串函數(shù)fgets從指定文件讀入一個字符串,該文件必須是以讀或讀寫方式打開的。fgets函數(shù)的調(diào)用形式為:fgets(str,n,fp);【說明】參數(shù)str可以是一個字符型數(shù)組名或指向字符串的指針;參數(shù)n為讀取的最多的字符個數(shù);參數(shù)fp為要讀取文件的指針。字符串讀寫函數(shù)-fgets【功能】從fp指定的文件中讀取長度不超過n-1個字符的字符串,并將該字符串放到字符數(shù)組str中。讀取成功,函數(shù)返回字符數(shù)組str的首地址;如果文件結(jié)束或出錯,則返回NULL。讀取操作遇到以下情況結(jié)束:①已經(jīng)讀取了n-1個字符;②當前讀取到的字符為回車符;③已讀取到文件末尾。注意使用該函數(shù)時,從文件讀取的字符個數(shù)不會超過n-1個,這是由于在字符串尾部還需自動追加一個“\0”字符,這樣讀取到的字符串在內(nèi)存緩沖區(qū)正好占有n個字節(jié)。如果從文件中讀取到回車符時,也作為一個字符送入由str所指的內(nèi)存緩沖區(qū),然后再向緩沖區(qū)送入一個“\0”字符。應注意,fgets()函數(shù)在使用stdin作為fp參數(shù)時與gets()函數(shù)功能有所不同:gets()把讀取到的回車符轉(zhuǎn)換成“\0”字符,而fgets()把讀取到的回車符作為字符存儲,然后再在末尾追加“\0”字符。注意假設文件readme.txt的內(nèi)容如下:有數(shù)組charstr[8];,文件指針fp指向readme.txt,讀寫位置指向字符c。運行語句fgets(str,8,fp);后str的內(nèi)容為:再次運行fgets(str,8,fp);后str的內(nèi)容為:第3次運行fgets(str,8,fp);后str的內(nèi)容為:字符串輸出函數(shù)fputsfputs函數(shù)把一個字符串輸出到磁盤文件上。其一般形式為:
fputs(str,fp);【說明】str可以是指向字符串的指針或字符數(shù)組名,也可以是字符串常量;fp為指向?qū)懭胛募闹羔??!竟δ堋繉⒂蓅tr指定的字符串寫入fp所指向的文件中。注意與fgets()函數(shù)在輸入字符串時末尾自動追加“\0”字符的特性相對應,fputs()函數(shù)在將字符串寫入文件時,其末尾的“\0”字符自動舍去。當fputs()函數(shù)使用stdout作為fp參數(shù)時,即fputs(str,stdout)與puts(str)在功能上有所不同:fputs()舍棄輸出字符串末尾加入的“\0”字符,而puts()把它轉(zhuǎn)換成回車符輸出。正常操作時,返回值為寫入的字符個數(shù);出錯時,返回值為EOF(-1)?!纠?1-4】將鍵盤輸入的若干行字符存入到磁盤文件result.txt中#include<stdio.h>voidmain(){FILE*fp; charstr[101];if((fp=fopen("result.txt","w"))==NULL){printf("filecreatederror.\n"); exit(0); }while(strlen(gets(str))>0)/*讀取字符串,輸入空串時結(jié)束*/{fputs(str,fp);/*寫到文件fp中*/ fputs("\n",fp);}fclose(fp);}【例11-5】編程完成將文本文件readme.txt復制到result.txt中。#include<stdio.h>voidmain(){FILE*fp1,*fp2; charstr[20];if((fp1=fopen("readme.txt","r"))==NULL){printf("file1opennederror.\n"); exit(0); }if((fp2=fopen("result.txt","w"))==NULL){printf("file2createderror.\n"); exit(0);}while(fgets(str,20,fp1)!=NULL)fputs(str,fp2);fclose(fp1);fclose(fp2);}數(shù)據(jù)塊讀寫函數(shù)文件數(shù)據(jù)塊讀函數(shù)freadfread函數(shù)用來從指定文件中讀取一個指定字節(jié)的數(shù)據(jù)塊。它的一般調(diào)用形式為fread(buffer,size,count,fp);【說明】buffer為讀入數(shù)據(jù)在內(nèi)存中存放的起始地址;size為每次要讀取的字符數(shù);count為要讀取的次數(shù);fp為文件類型指針。【功能】在fp指定的文件中讀取count次數(shù)據(jù)項(每次size個字節(jié))存放到以buffer所指的內(nèi)存單元地址中。數(shù)據(jù)塊讀寫函數(shù)文件數(shù)據(jù)塊寫函數(shù)fwritefwrite函數(shù)用來將數(shù)據(jù)輸出到磁盤文件上。它的一般調(diào)用形式為fwrite(buffer,size,count,fp);【說明】buffer為輸出數(shù)據(jù)在內(nèi)存中存放的首地址;size為每次要輸出到文件中的字節(jié)數(shù);count為要輸出的次數(shù);fp為文件類型指針?!竟δ堋繉腷uffer為首地址的內(nèi)存中取出count次數(shù)據(jù)項(每次size個字節(jié))寫入fp所指的磁盤文件中注意當文件以二進制形式打開時,fread函數(shù)就可以讀取任何類型的信息。例如:fread(array,4,5,fp);其中,array為一個實型數(shù)組名,一個實型量占4個字節(jié)。該函數(shù)從fp所指的數(shù)據(jù)文件中讀取5次4字節(jié)的實型數(shù)據(jù),存儲到數(shù)組array中。fread()函數(shù)讀取的數(shù)據(jù)塊的總字節(jié)數(shù)應該是size*count個字節(jié)。正常操作時函數(shù)的返回值為讀取的項數(shù),出錯時為-1。注意當文件以二進制形式打開時,fwrite函數(shù)就可以寫入任何類型的信息。例如:fwrite(array,2,10,fp);其中,array為一個整型數(shù)組名,一個整型量占兩個字節(jié)。該函數(shù)將整型數(shù)組中10個兩字節(jié)的整型數(shù)據(jù)寫入由fp所指的磁盤文件中。與fread()函數(shù)一樣寫入的數(shù)據(jù)塊的總字節(jié)是size*count個字節(jié)。正常操作時返回值為寫入的項數(shù),出錯時返回值為-1。【例11-6】編程從鍵盤輸入3個學生的數(shù)據(jù),將它們存入到文件result.dat中,然后再讀出顯示在屏幕上。#include<stdio.h>#dedefineSIZE3structstudent{intno;charname[10];intage;charaddress[20];}stud[SIZE],fout;voidstudent_save(){inti;FILE*fp;if((fp=fopen("result.dat","wb"))==NULL) {printf("filecreatederror.\n");return;}for(i=0;i<SIZE;i++)/*寫學生的信息*/{if(fwrite(&stud[i],sizeof(structstudent),1,fp)!=1)printf("filewriteerror.\n");}fclose(fp);}【例11-6】編程從鍵盤輸入3個學生的數(shù)據(jù),將它們存入到文件result.dat中,然后再讀出顯示在屏幕上。voidstudent_display(){FILE*fp; inti;if((fout=fopen("result.dat","rb"))==NULL{printf("fileopennederror.\n");return;}printf("No.NameAgeAddress\n");while(fread(&fout,sizeof(fout),1,fp))printf("%4d%-10s%4d%-20s",fout.no,,fout.age,fout.address);fclose(fp);}【例11-6】編程從鍵盤輸入3個學生的數(shù)據(jù),將它們存入到文件result.dat中,然后再讀出顯示在屏幕上。voidmain(){inti;for(i=0;i<SIZE;i++)/*從鍵盤讀入學生的信息(結(jié)構(gòu))*/{printf("Pleaseinputstudent%d:",i+1);scanf("%d%s%d%s",&stud[i].no,stud[i].name,&stud[i].age,stud[i].address);}student_save();student_display();}
格式化輸入輸出函數(shù)fprintf函數(shù)和fscanf函數(shù)格式化輸入函數(shù)fscanf函數(shù)調(diào)用的格式為fscanf(fp,格式控制串,輸入列表);【說明】fp是指向要讀取文件的文件型指針,格式控制串,輸出列表同scanf函數(shù)?!竟δ堋繌膄p指向的文件中,按格式控制串中的控制符讀取相應數(shù)據(jù)賦給輸入列表中對應的變量地址中。格式化輸入輸出函數(shù)fprintf函數(shù)和fscanf函數(shù)格式化輸出函數(shù)fprintf函數(shù)調(diào)用的格式為
fprintf(fp,格式控制串,輸出列表);【說明】fp是指向要寫入文件的文件型指針,格式控制串,輸出列表同printf函數(shù)?!竟δ堋繉⑤敵隽斜碇械母鱾€變量或常量,依次按格式控制串中的控制符說明的格式寫入fp指向的文件中。說明用fprintf和fscanf函數(shù)對磁盤文件讀寫,使用方便,容易理解,但由于在輸入輸出時要進行ASCII碼和二進制的轉(zhuǎn)換,時間開銷大,因此,在內(nèi)存與磁盤頻繁交換數(shù)據(jù)的情況下,最好不用fprintf和fscanf函數(shù),而用fread和fwrite函數(shù)。其他讀寫函數(shù)(字)整數(shù)輸入輸出函數(shù)getw和putwputw和getw用來對磁盤文件讀寫一個字(整數(shù))。例如:putw(100,fp);它的作用是將整數(shù)100輸出到fp所指的文件,而i=getw(fp);作用是從磁盤文件中讀一個整數(shù)到內(nèi)存,賦給整型變量i。讀寫其他類型數(shù)據(jù)對于系統(tǒng)沒有提供函數(shù)的和不能方便完成的讀寫操作,用戶可以自定義讀寫函數(shù),這樣的函數(shù)具有很好的針對性。11.3文件的定位11.3.1置文件位置指針于文件開頭位置的函數(shù)
rewind()rewind()函數(shù)的一般調(diào)用形式為:rewind(fp);【說明】fp是指向由fopen函數(shù)打開的文件指針【功能】使位置指針重新返回文件的開頭,此函數(shù)沒有返回值。【例11-7】有一磁盤文件readme.txt,首先將其內(nèi)容顯示在屏幕上,然后把它復制到另一文件result.txt上。
#include"stdio.h"voidmain(){FILE*fp1,*fp2;if((fp1=fopen("readme.txt","r"))==NULL){printf("fileopennederror.\n");exit(0);}if((fp2=fopen("result.txt","w"))==NULL){printf("filecreatederror.\n");exit(0);}while(!feof(fp1))putchar(fgetc(fp1));rewind(fp1);/*重置文件位置指針至文件頭*/while(!feof(fp1)) fputc(fgetc(fp1),fp2);fclose(fp1);fclose(fp2);}11.3.2改變文件位置指針位置的函數(shù)fseekfseek函數(shù)的調(diào)用形式為:fseek(fp,offset,whence);【說明】fp為指向當前文件的指針;offset為文件位置指針的位移量,指以起始位置為基準值向前移動的字節(jié)數(shù),要求offset為long型數(shù)據(jù);whence為起始位置,用整型常量表示,ANSIC規(guī)定它必須是0、1或2之一值,它們表示三個符號常數(shù),在stdio.h中定義如下:名字值起始位置SEEK_SET0文件開頭SEEK_CUR1文件當前位置SEEK_END2文件末尾11.3.2改變文件位置指針位置的函數(shù)fseek【功能】將文件位置指針移到由起始位置(whence)開始、位移量為offset的字節(jié)處。如果函數(shù)讀寫指針移動失敗,返回值為-1。fseek函數(shù)一般用于二進制文件,因為文本文件要發(fā)生字符轉(zhuǎn)換,計算位置時往往會發(fā)生混亂。下面是fseek函數(shù)調(diào)用的幾個例子:fseek(fp,100L,0);/*將位置指針移到離文件頭100個字節(jié)處*/fseek(fp,50L,1);/*將位置指針移到離當前位置50個字節(jié)處*/fseek(fp,-20L,2);/*將位置指針從文件末尾處向后退20個字節(jié)*/注意偏移量為長整型,如100L。利用fseek函數(shù)就可以實現(xiàn)隨機讀寫。11.3.3取得文件當前位置的函數(shù)ftell
ftell函數(shù)的作用是得到流式文件中的當前位置,用相對于文件開頭的位移量來表示。由于文件中的位置指針經(jīng)常移動,人們往往不容易辨清其當前位置。用ftell函數(shù)可以得到當前位置。如果ftell函數(shù)返回值為-1L,則表示出錯。例如:if(ftell(fp)==-1L)printf("error\n");11.3.4文件的錯誤檢測文件讀寫錯誤檢測函數(shù)在調(diào)用各種輸入輸出函數(shù)(如fputc、fgetc、fread、fwrite等)時,如果出現(xiàn)錯誤,則除了函數(shù)返回值有所反映外,還可以用ferror函數(shù)檢查,它的一般調(diào)用形式為:ferror(fp);如果ferror返回值為0(假),則表示未出錯。如果返回一個非0值,則表示出錯。應該注意,對同一個文件,每一次調(diào)用輸入輸出函數(shù),均產(chǎn)生一個新的ferror函數(shù)值,因此,應當在調(diào)用一個輸入輸出函數(shù)后立即檢查ferror函數(shù)的值,否則信息會丟失。在執(zhí)行fopen函數(shù)時,ferror函數(shù)的初始值自動置為0。11.3.4文件的錯誤檢測清除文件錯誤標志函數(shù)clearerr函數(shù)的作用是使文件錯誤標志和文件結(jié)束標志置為0。假設在調(diào)用一個輸入輸出函數(shù)時出現(xiàn)錯誤,ferror函數(shù)值為一個非0值。在調(diào)用clearerr(fp)后,ferror(fp)的值變成0。只要出現(xiàn)錯誤標志,就一直保留,直到對同一文件調(diào)用clearerr函數(shù)或rewind函數(shù),或任何其他一個輸入輸出函數(shù)。11.4編譯預處理編譯預處理是指在進行編譯的第一遍掃描(詞法掃描和語法分析)之前所作的工作。預處理是C語言的一個重要功能,它由預處理程序負責完成。當對一個源文件進行編譯時,系統(tǒng)將自動引用預處理程序?qū)υ闯绦蛑械念A處理部分作處理,處理完畢自動進入對源程序的編譯,過程如下圖:11.4編譯預處理C語言提供了多種預處理功能,如宏定義、文件包含、條件編譯等。合理地使用預處理功能編寫的程序便于閱讀、修改、移植和調(diào)試,也有利于模塊化程序設計。預處理的命令有以下幾個特點:預處理命令均以#開頭,結(jié)尾不加分號;預處理命令可以放在程序中任何位置,作用范圍從定義處到文件結(jié)尾。本章介紹常用的幾種預處理功能。11.4.1宏定義宏提供了用一個標識符來表示一個字符串的機制,實際上就是一種替換,有時稱為宏替換。在編譯預處理時,對程序中所有出現(xiàn)的“宏”,都用宏定義中的字符串去代換。宏定義由宏定義命令完成,宏代換是由預處理程序自動完成的。宏分為有參數(shù)和無參數(shù)兩種。無參宏定義無參宏的宏名后不帶參數(shù)。其定義的一般形式為:
#define
標識符字符串【說明】define為宏定義命令,標識符為所定義的宏名,字符串可以是常數(shù)、表達式、格式串等?!纠?1-8】計算圓的面積和周長。#include<stdio.h>#definePI3.14159voidmain(){floats,l,r;printf("inputr:");scanf("%f",&r);s=PI*r*r;l=2*PI*r;printf("s=%f,l=%f\n",s,l);}注意宏定義是用宏名來表示一個字符串,在宏展開時又以該字符串取代宏名,這只是一種簡單的代換,字符串中可以含任何字符,可以是常數(shù),也可以是表達式,預處理程序?qū)λ蛔魅魏螜z查。如有錯誤,只能在編譯已被宏展開后的源程序時發(fā)現(xiàn)。宏定義不是說明或語句,在行末不必加分號,如果加上分號則連分號也一起置換。注意宏定義必須寫在函數(shù)之外,其作用域為宏定義命令起到源程序結(jié)束。如要終止其作用域可使用#undef命令,例如:#definePI3.14159voidmain(){①}#undefPI②PI只在①中有效,在②中無效。注意宏名在源程序中若用引號括起來,則預處理程序不對其作宏代換。#definePI3.14159voidmain(){printf("PI");…}程序的運行結(jié)果為:PI,而不是3.14159。宏定義允許嵌套。在宏展開時由預處理程序?qū)訉哟鷵Q。例如:#definePI3.14159#defineSPI*y*y/*PI是已定義的宏名*/習慣上宏名用大寫字母表示,以便于與變量區(qū)別。帶參宏定義格式:#define標示符(形參表)形參表達式例如:#defineMAX(a,b)(a>b)?(a):(b)進行宏替換時,可以像使用函數(shù)一樣,通過實參與形參傳遞數(shù)據(jù)?!纠?1-9】計算1到10的平方和。#include<stdio.h>#defineFUN(a)a*avoidmain(){inti;ints=0;for(i=1;i<=10;i++)s=s+FUN(i);printf("%d\n",s);}注意宏名和括號之間不能有空格。有些參數(shù)表達式必須加括號,否則會出現(xiàn)替換錯誤,例如#defineS(x)x*x則S(5+6)并不是11的平方,而是:5+6*5+6結(jié)果為41。而如果宏定義為#defineS(x)(x)*(x)S(5+6)就會被替換為:(5+6)*(5+6)從而符合設計的要求了。這樣的問題在無參宏定義時也要注意:注意函數(shù)要求實參與形參類型一致,而宏替換不需要。函數(shù)只有一個返回值,而宏替換可能有多個。函數(shù)影響運行時間,而宏替換只影響編譯時間。
11.4.2文件包含文件包含是把指定的文件插入該命令行位置取代該命令行。命令的一般形式為:#include<文件名>格式1或#include"文件名"格式2例如:#include"stdio.h"#include"math.h"注意使用格式1時,預處理程序在C編譯系統(tǒng)定義的標準目錄下查找指定的文件;使用格式2時,預處理程序首先在當前源文件所在目錄下查找指定文件,如沒找到,則在C編譯系統(tǒng)定義的標準目錄下查找指定的文件;一個#include命令只能包含一個文件,而且必須是文本文件文件包含可以嵌套,如a包含b,b包含c文件包含在程序設計中非常有用,像C語言中的頭文件,其中定義了很多外部變量或宏,在設計程序時只要包含進來就可以了,不需要重復定義,節(jié)省了工作量,又可以避免出錯。11.4.3條件編譯條件編譯有三種形式,第一種形式:#ifdef
標識符程序段1#else
程序段2#endif
【功能】如果標識符已被#define命令定義過則對程序段1進行編譯;否則對程序段2進行編譯。如果沒有程序段2(它為空),本格式中的#else可以沒有,即可以寫為:#ifdef
標識符程序段#endif
【例11-10】條件編譯示例。#include<stdio.h>#defineNUM2008structstudent{intno;char*name;charsex;floatscore;}*s;voidmain(){s=(structstudent*)malloc(sizeof(structstudent));s->no=102;s->name="Zhangping";s->sex='M';s->score=62.5;#ifdefNUMprintf("Number=%d\nScore=%f\n",s->no,s->score);#elseprintf("Name=%s\nSex=%c\n",s->name,s->sex);#endiffree(s);}11.4.3條件編譯第二種形式:#ifndef標識符程序段1#else
程序段2#endif與第一種形式的區(qū)別是將“ifdef”改為“ifndef”。它的功能是,如果標識符未被#define命令定義過則對程序段1進行編譯,否則對程序段2進行編譯。這與第一種形式的功能正相反。11.4.3條件編譯第三種形式:
#if常量表達式
程序段1
#else
程序段2
#endif【功能】如常量表達式值為真(非0),則對程序段1進行編譯,否則對程序段2進行編譯。11.5程序舉例【例11-11】將文件readme.txt中所有大寫字母改寫成小寫字母后保存,文件中其他字符不變。程序如下:
#include"stdio.h“
#include"stdlib.h“
#include"ctype.h“
main()
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 假期讀一本好書讀后感900字(12篇)
- 2024全新能源開發(fā)項目投資與合作合同
- 中式快餐創(chuàng)業(yè)計劃書
- 2024年工業(yè)設備維修協(xié)議
- 2024年度4S店租賃期內(nèi)公共區(qū)域維護與管理協(xié)議
- 2024年建筑工程消防設計與施工合同
- 2024年企業(yè)廣告發(fā)布與媒體投放合同
- 2024年大數(shù)據(jù)分析與應用服務協(xié)議
- 2024年度「惠州技術(shù)開發(fā)」合同標的:技術(shù)研發(fā)與成果共享
- 2024年工程項目混凝土供應合同
- 常規(guī)弱電系統(tǒng)施工單價表純勞務
- 2024年代持法人報酬協(xié)議書模板范本
- 2024年貴州貴陽市信訪局招聘歷年高頻難、易錯點500題模擬試題附帶答案詳解
- 2024年人教版六年級數(shù)學上冊《第5單元第7課時 扇形的認識》單元整體教學課件
- 2023湖南文藝出版社五年級音樂下冊全冊教案
- 創(chuàng)作志愿者文化衫
- 國開2024秋《形勢與政策》專題測驗1-5參考答案
- 【PPP項目風險評估與控制探究的國內(nèi)外文獻綜述3900字】
- 異常情況報告制度-異常情況處理制度
- 《新課標引領(lǐng)、新教材啟航》初中化學講座 課件
- 人教版初中化學九年級上冊第六單元課題1 碳單質(zhì)的多樣性(第一課時)
評論
0/150
提交評論