C語言程序設(shè)計(jì)教程(四)233_第1頁
C語言程序設(shè)計(jì)教程(四)233_第2頁
C語言程序設(shè)計(jì)教程(四)233_第3頁
C語言程序設(shè)計(jì)教程(四)233_第4頁
C語言程序設(shè)計(jì)教程(四)233_第5頁
已閱讀5頁,還剩228頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

C語言程序設(shè)計(jì)教程(四)第10章文件第11章位運(yùn)算第12章編譯預(yù)處理第13章C語言的實(shí)際應(yīng)用第10章文件所謂“文件〞〔file〕是指計(jì)算機(jī)外部存儲介質(zhì)〔磁盤、磁帶等〕上建立的數(shù)據(jù)集合。這個數(shù)據(jù)集有一個名稱,叫做文件名。本章主要介紹文件的根本操作常識,以及文件操作的相關(guān)函數(shù)。10.1文本流和二進(jìn)制流在C中引入了流(stream)的概念。它將數(shù)據(jù)的輸入輸出看作是數(shù)據(jù)的流入和流出,這樣不管是磁盤文件或者是物理設(shè)備(如:打印機(jī)、顯示器、鍵盤等),都可看作一種流的源和目的,視他們?yōu)橥环N東西,而不管其具體的物理結(jié)構(gòu)體,即對他們的操作就是數(shù)據(jù)的流入和流出。這種把數(shù)據(jù)的輸入、輸出的操作對象,抽象化為一種流,而不管它的具體結(jié)構(gòu)體的方法很有利于編程,而涉及流的輸出操作函數(shù)可用于各種對象,與其具體的實(shí)體無關(guān),即具有通用性。在C中流可分為兩大類,即文本流(text

stream)和二進(jìn)制流(binary

stream)。所謂文本流是指在流中流動的數(shù)據(jù)是以字符形式出現(xiàn)的。在文本流中,'\n'被換成回車CR和換行LF的代碼0xDH和0xAH。而當(dāng)輸出時,那么0xDH和0xAH被換成'\n'。二進(jìn)制流是指流動的是二進(jìn)制數(shù)字序列,假設(shè)流中有字符,那么用一個字節(jié)的二進(jìn)制ASCII碼表示,假設(shè)是數(shù)字,那么用一個字節(jié)的二進(jìn)制數(shù)表示。在流入流出時,對'\n'符號不進(jìn)行變換。10.1文本流和二進(jìn)制流(續(xù))例如2001這個數(shù),在文本流中用其ASCII碼表示為:共占4字節(jié)。而在二進(jìn)制流中那么表示為:00000111

11010001

用十六進(jìn)制就是07D1。只占兩字節(jié)。由此看出,二進(jìn)制流比文本流節(jié)省空間,且不用進(jìn)行對'\n'的轉(zhuǎn)換,這樣可以大大加快流的速度,提高效率。因而,對于含有大量數(shù)字信息的數(shù)字流,可以采用二進(jìn)制流的方式進(jìn)行存儲;對于含有大量字符信息的流,那么采用文本流的方式進(jìn)行存儲。'2'

'0'

'0'

'1'||||50

48

48

49圖10-1文本流存儲結(jié)構(gòu)〔2001〕10.2文件FILE的數(shù)據(jù)結(jié)構(gòu)體typedefstruct{ shortlevel; unsignedflags; charfd; unsignedcharhold; shortbsize; unsignedchar*buffer; unsignedchar*curp; unsignedistemp; shorttoken;}FILE;10.2文件FILE的數(shù)據(jù)結(jié)構(gòu)體(續(xù))這是C語言中使用的聲明(在stdio.h文件中),不同的C編譯器,可能使用不同的聲明,但根本含義變化不會太大。flags:

是一個10位的標(biāo)志字,其具體含義如下:

代表符號

含義

0

_F_READ

1

_F_WRIT

2

_F_BUF

由fclose釋放緩沖區(qū)

3

_F_LBUF

行緩沖

4

_F_ERR

出錯標(biāo)志

5

_F_EOF

EOF文件尾標(biāo)志

6

_F_BIN

二進(jìn)制方式

7

_F_IN

在進(jìn)行輸入

8

_F_OUT

在進(jìn)行輸出

9

_F_TERM

文件是一個終端10.2文件FILE的數(shù)據(jù)結(jié)構(gòu)體(續(xù))其他各字段內(nèi)容以及flags字段內(nèi)各位所表示的功能,請參照其他一些資料。注意:不要把文件指針和FILE結(jié)構(gòu)體指針混為一談,它們代表兩個不同的地址。文件指針指出了對文件當(dāng)前讀寫的數(shù)據(jù)位置,而FILE結(jié)構(gòu)體指針是指出了翻開文件所對應(yīng)的FILE結(jié)構(gòu)體在內(nèi)存中的地址,這個指針?biāo)鼘?shí)際本身也包含了文件指針的信息。流指針中的各字段是供C語言內(nèi)部使用的,用戶不應(yīng)該存取它的任何字段。10.3.1文件的翻開(fopen()函數(shù))fopen()函數(shù)用來翻開一個文件,其調(diào)用的一般形式為:文件指針名=fopen(文件名,使用文件方式);其中:文件指針名必須是被說明為FILE類型的指針變量;文件是被翻開文件的文件名;使用文件方式是指文件的類型和操作要求;文件名是字符串常量或字符串?dāng)?shù)組。10.3.1文件的翻開(fopen()函數(shù))(續(xù))例如:FILE*fp;fp=("file.a","r");其意義是:在當(dāng)前目錄下翻開文件file.a,只允許進(jìn)行讀操作,并使文件指針fp指向該文件。又如:FILE*fphzk;fphzk=("c:\\hzk16","rb");其意義是:翻開C驅(qū)動器磁盤的根目錄下的文件hzk16,這是一個二進(jìn)制文件,只允許按二進(jìn)制方式進(jìn)行讀操作。兩個反斜線〞\\〞中的第一個表示轉(zhuǎn)義字符,第二個表示根目錄,并使文件指針fphzk指向該文件。10.3.1文件的翻開(fopen()函數(shù))(續(xù))使用文件的方式共有12種,下面給出了它們的符號和意義。文件使用方式意義“rt”只讀打開一個文本文件,只允許讀數(shù)據(jù)“wt”只寫打開或建立一個文本文件,只允許寫數(shù)據(jù)“at”追加打開一個文本文件,并在文件末尾寫數(shù)據(jù)“rb”只讀打開一個二進(jìn)制文件,只允許讀數(shù)據(jù)“wb”只寫打開或建立一個二進(jìn)制文件,只允許寫數(shù)據(jù)“ab”追加打開一個二進(jìn)制文件,并在文件末尾寫數(shù)據(jù)“rt+”讀寫打開一個文本文件,允許讀和寫“wt+”讀寫打開或建立一個文本文件,允許讀寫“at+”讀寫打開一個文本文件,允許讀,或在文件末追加數(shù)據(jù)“rb+”讀寫打開一個二進(jìn)制文件,允許讀和寫“wb+”讀寫打開或建立一個二進(jìn)制文件,允許讀和寫“ab+”讀寫打開一個二進(jìn)制文件,允許讀,或在文件末追加數(shù)據(jù)10.3.1文件的翻開(fopen()函數(shù))(續(xù))對于文件使用方式有以下幾點(diǎn)說明:文件使用方式由r,w,a,t,b,+六個字符拼成,各字符的含義是:r(read):讀w(write):寫a(append):追加t(text):文本文件,可省略不寫b(banary):二進(jìn)制文件+:讀和寫10.3.1文件的翻開(fopen()函數(shù))(續(xù))凡用〞r〞翻開一個文件時,該文件必須已經(jīng)存在,且只能從該文件讀出。用〞w〞翻開的文件只能向該文件寫入。假設(shè)翻開的文件不存在,那么以指定的文件名建立該文件,假設(shè)翻開的文件已經(jīng)存在,那么將該文件刪去,重建一個新文件。假設(shè)要向一個已存在的文件追加新的信息,只能用〞a〞方式翻開文件。但此時該文件必須是存在的,否那么將會出錯。在翻開一個文件時,如果出錯,fopen()函數(shù)將返回一個空指針值NULL。在程序中可以用這一信息來判別是否完成翻開文件的工作,并作相應(yīng)的處理。因此常用以下程序段翻開文件:if((fp=fopen("c:\\hzk16","rb"))==NULL){ printf("\nerroronopenc:\\hzk16file!"); getch(); exit(1);}10.3.1文件的翻開(fopen()函數(shù))(續(xù))這段程序的意義是:如果返回的指針為空,表示不能翻開C盤根目錄下的hzk16文件,那么給出提示信息〞erroronopenc:\hzk16file!〞,下一行g(shù)etch()的功能是從鍵盤輸入一個字符,但不在屏幕上顯示。在這里,該行的作用是等待,只有當(dāng)用戶從鍵盤敲任一鍵時,程序才繼續(xù)執(zhí)行,因此用戶可利用這個等待時間閱讀出錯提示。敲鍵后執(zhí)行exit(1)退出程序。把一個文本文件讀入內(nèi)存時,要將ASCII碼轉(zhuǎn)換成二進(jìn)制碼,而把文件以文本方式寫入磁盤時,也要把二進(jìn)制碼轉(zhuǎn)換成ASCII碼,因此文本文件的讀寫要花費(fèi)較多的轉(zhuǎn)換時間。而對二進(jìn)制文件的讀寫不存在這種轉(zhuǎn)換。標(biāo)準(zhǔn)輸入文件(鍵盤),標(biāo)準(zhǔn)輸出文件(顯示器),標(biāo)準(zhǔn)出錯輸出(出錯信息)是由系統(tǒng)翻開的,可直接使用。10.3.2關(guān)閉文件函數(shù)fclose()文件操作完成后,必須要用fclose()函數(shù)進(jìn)行關(guān)閉,這是因?yàn)閷Ψ_的文件進(jìn)行寫入時,假設(shè)文件緩沖區(qū)的空間未被寫入的內(nèi)容填滿,這些內(nèi)容將不會寫到翻開的文件中去,從而造成喪失數(shù)據(jù)。只有對翻開的文件進(jìn)行關(guān)閉操作時,停留在文件緩沖區(qū)的內(nèi)容才能被寫到該文件中去,從而使文件完整。再者一旦關(guān)閉了文件,該文件對應(yīng)的FILE結(jié)構(gòu)體將被釋放,從而使關(guān)閉的文件得到保護(hù),因?yàn)檫@時對該文件的存取操作將不會進(jìn)行。文件的關(guān)閉也意味著釋放了該文件的緩沖區(qū)。fclose()函數(shù)用來關(guān)閉一個文件,其調(diào)用的一般形式為:fclose(文件指針名);10.3.2關(guān)閉文件函數(shù)fclose()(續(xù))例如:intfclose(FILE*stream);它表示fclose()函數(shù)將關(guān)閉FILE指針stream對應(yīng)的文件,并返回一個整數(shù)值。假設(shè)成功地關(guān)閉了文件,那么返回一個0值,否那么返回一個非0值。因此,常用以下方法進(jìn)行測試:if(fclose(fp)!=0){ printf("Filecannotbeclosed\n"); exit(1);}elseprintf("Fileisnowclosed\n");10.3.2關(guān)閉文件函數(shù)fclose()(續(xù))當(dāng)翻開多個文件進(jìn)行操作,而又要同時關(guān)閉時,可采用fcloseall()函數(shù),它將關(guān)閉所有在程序中翻開的文件。fcloseall()函數(shù)用來關(guān)閉在程序中翻開的所有文件,其調(diào)用的一般形式為:intfcloseall();該函數(shù)將關(guān)閉所有已翻開的文件,將各文件緩沖區(qū)未裝滿的內(nèi)容寫到相應(yīng)的文件中去,接著釋放這些緩沖區(qū),并返回關(guān)閉文件的數(shù)目。如關(guān)閉了4個文件,那么當(dāng)執(zhí)行〞n=fcloseall();〞語句時,n應(yīng)為4。10.3.3文件的讀寫(1).讀寫文件中字符的函數(shù)(一次只讀寫文件中的一個字符)intfgetc(FILE*stream);intfgetchar(void);intfputc(intch,FILE*stream);intfputchar(intch);intgetc(FILE*stream);intputc(intch,FILE*stream);其中fgetc()函數(shù)將把由流指針stream指向的文件中的一個字符讀出。例如:ch=getc(fp);其含義是:將把流指針fp指向的文件中的一個字符讀出,并賦給ch,當(dāng)執(zhí)行fgetc()函數(shù)時,假設(shè)當(dāng)時文件指針指到文件尾,即遇到文件結(jié)束標(biāo)志EOF(其對應(yīng)值為-1),該函數(shù)返回一個-1給ch,在程序中常用檢查fgetc()函數(shù)返回值是否為-1來判斷是否已讀到文件尾,從而決定是否繼續(xù)。10.3.3文件的讀寫(續(xù))例10-1文件讀寫函數(shù)應(yīng)用舉例1。#include<stdio.h>#include<stdlib.h>voidmain(){ FILE*fp; charch; if((fp=fopen("myfile.txt","r"))==NULL) { printf("filecannotbeopened\n"); exit(0); } while((ch=fgetc(fp))!=EOF) fputc(ch,stdout); fclose(fp);}10.3.3文件的讀寫(續(xù))運(yùn)行結(jié)果如下:HelloWorld!其中myfile.txt文件為當(dāng)前目錄下已存在的文件。該程序以只讀方式翻開myfile.txt文件,在執(zhí)行while循環(huán)時,文件指針每循環(huán)一次后移一個字符位置。用fgetc()函數(shù)將文件指針fp指定的字符讀到ch變量中,然后用fputc()函數(shù)在屏幕上顯示,當(dāng)讀到文件結(jié)束標(biāo)志EOF時,關(guān)閉該文件。上面的程序用到了fputc()函數(shù),該函數(shù)將字符變量ch的值寫到流指針指定的文件中去,由于流指針用的是標(biāo)準(zhǔn)輸出(顯示器)的FILE指針stdout,故讀出的字符將在顯示器上顯示。又比方:fputc(ch,fp);該函數(shù)執(zhí)行的結(jié)果是將把ch表示的字符送到流指針fp指向的文件中去。putchar(c)相當(dāng)于fputc(c,stdout);10.3.3文件的讀寫(續(xù))getchar()相當(dāng)于fgetc(stdin)。注意:這里使用char

ch,其實(shí)是不科學(xué)的,因?yàn)樽詈笈袛嘟Y(jié)束標(biāo)志時,是看ch!=EOF,而EOF的值為-1,這顯然和char是不能比較的。所以,某些使用,我們都聲明成int

ch。(2).讀寫文件中字符串的函數(shù)char*fgets(char*string,intn,FILE*stream);char*gets(char*s);intfprintf(FILE*stream,char*format,variable-list);intfputs(char*string,FILE*stream);char*puts(char*s);intfscanf(FILE*stream,char*format,variable-list);10.3.3文件的讀寫(續(xù))其中fgets()函數(shù)將把由流指針stream指定的文件中的n-1個字符,讀到由指針string指向的字符數(shù)組中去。例如:fgets(buffer,9,fp);將把fp指向的文件中的8個字符讀到buffer內(nèi)存區(qū),buffer可以是聲明的字符數(shù)組,也可以是動態(tài)分配的內(nèi)存區(qū)。注意:fgets()函數(shù)讀到'\n'就停止,而不管是否到達(dá)數(shù)目要求。同時在讀取字符串的最后加上'\0'。fgets()函數(shù)執(zhí)行完以后,返回一個指向該串的指針。如果讀到文件尾或出錯,那么均返回一個空指針NULL,所以長用feof()函數(shù)來測定是否到了文件尾或者是ferror()函數(shù)來測試是否出錯,例如下面的程序用fgets()函數(shù)讀test.txt文件中的第一行并顯示出來。10.3.3文件的讀寫(續(xù))例10-2fgets()函數(shù)應(yīng)用舉例。#include<stdio.h>#include<stdlib.h>voidmain(){ FILE*fp; charstr[128]; if((fp=fopen("test.txt","r"))==NULL) { printf("cannotopenfile\n"); exit(1); }10.3.3文件的讀寫(續(xù)) while(!feof(fp)) { if(fgets(str,128,fp)!=NULL) printf("%s",str); } fclose(fp);}運(yùn)行結(jié)果:HelloChina!HelloStudent!10.3.3文件的讀寫(續(xù))gets()、puts()函數(shù)專用于標(biāo)準(zhǔn)輸入、輸出設(shè)備的字符串讀寫。在執(zhí)行時,只要未遇到換行符或文件結(jié)束標(biāo)志,將一直繼續(xù)執(zhí)行下去。因此執(zhí)行到什么時候?yàn)橹梗枰脩暨M(jìn)行控制,否那么可能造成存儲區(qū)的溢出。fputs()函數(shù)向流指針stream指定的文件寫入一個由string指向的字符串,'\0'不寫入文件。fprintf()和fscanf()同printf()和scanf()函數(shù)類似,不同之處就是printf()函數(shù)是向顯示器輸出,fprintf()那么是向流指針stream指向的文件輸出;fscanf()那么是向流指針stream指向的文件輸入。例10-3向文件test.dat里輸入一些字符。#include<stdio.h>#include<stdlib.h>10.3.3文件的讀寫(續(xù))voidmain(){ char*s="That'sgoodnews"; inti=617; FILE*fp; fp=fopen("test.dat","w");/*建立一個文字文件只寫*/fputs("YourscoreofTOEFLis",fp);/*向所建文件寫入一串字符*/fputc(':',fp);/*向所建文件寫冒號:*/fprintf(fp,"%d\n",i);/*向所建文件寫一整型數(shù)*/fprintf(fp,"%s",s);/*向所建文件寫一字符串*/fclose(fp);}10.3.3文件的讀寫(續(xù))用DOS的TYPE命令顯示test.dat的內(nèi)容如下所示:

Your

score

of

TOEFL

is:

617

↙That's

good

news↙例10-4把上面的文件test.dat里的內(nèi)容在屏幕上顯示出來。#include<stdio.h>#include<stdlib.h>voidmain(){ char*s,m[20]; inti; FILE*fp; s=(char*)malloc(sizeof(char)*100); fp=fopen("test.dat","r");/*翻開文字文件只讀*/10.3.3文件的讀寫(續(xù)) fgets(s,24,fp);/*從文件中讀取23個字符*/printf("%s",s); fscanf(fp,"%d",&i);/*讀取整型數(shù)*/ printf("%d",i); putchar(fgetc(fp));/*讀取一個字符同時輸出*/ fgets(m,17,fp);/*讀取16個字符*/ puts(m);/*輸出所讀字符串*/ free(s); fclose(fp);}運(yùn)行結(jié)果:YourscoreofTOEFLis:617That’sgoodnews10.3.3文件的讀寫(續(xù))(3).讀寫文件中數(shù)據(jù)塊的函數(shù)intfread(void*ptr,intsize,intnitems,FILE*stream);intfwrite(void*ptr,intsize,intnitems,FILE*stream);fread()函數(shù)從流指針stream指定的文件中讀取nitems個數(shù)據(jù)項(xiàng),每個數(shù)據(jù)項(xiàng)的長度為size個字節(jié),讀取的nitems數(shù)據(jù)項(xiàng)存入由ptr指針指向的內(nèi)存緩沖區(qū)中。在執(zhí)行fread()函數(shù)時,文件指針隨著讀取的字節(jié)數(shù)而向后移動,最后移動結(jié)束的位置等于實(shí)際讀出的字節(jié)數(shù)。該函數(shù)執(zhí)行結(jié)束后,將返回實(shí)際讀出的數(shù)據(jù)項(xiàng)數(shù),這個數(shù)據(jù)項(xiàng)數(shù)不一定等于設(shè)置的nitems,因?yàn)榧僭O(shè)文件中沒有足夠的數(shù)據(jù)項(xiàng),或讀中間出錯,都會導(dǎo)致返回的數(shù)據(jù)項(xiàng)數(shù)少于設(shè)置的nitems。當(dāng)返回數(shù)不等于nitems時,可以用feof()或ferror()函數(shù)進(jìn)行檢查。fwrite()函數(shù)從ptr指向的緩沖區(qū)中取出長度為size字節(jié)的nitems個數(shù)據(jù)項(xiàng),寫入流指針stream指向的文件中。執(zhí)行該操作后,文件指針將向后移動,移動的字節(jié)數(shù)等于寫入文件的字節(jié)數(shù)目。該函數(shù)操作完成后,也將返回寫入的數(shù)據(jù)項(xiàng)數(shù)。注意:在調(diào)用fwrite()函數(shù)和fread()函數(shù)時,應(yīng)該知道要操作的數(shù)據(jù)的類型格式,這樣才能把處理數(shù)據(jù)進(jìn)行寫入或讀入操作。10.3.3文件的讀寫(續(xù))例10-5從鍵盤輸入5個學(xué)生的信息,并用這些信息建立一個名為student.dat的磁盤文件。然后再從文件讀出信息并顯示再屏幕上。#include<stdio.h>#defineSIZE5structstudent{ charname[10]; intage; intscore; charaddr[30];};intsavedata(structstudentstua[],intm);voidread_print();10.3.3文件的讀寫(續(xù))voidmain(){ structstudentstua[SIZE]; for(inti=0;i<SIZE;i++) { printf("Enterstudentname,addr,age,score:"); scanf("%s",stua[i].name); getchar(); scanf("%s",stua[i].addr); getchar(); scanf("%d,%d",&stua[i].age,&stua[i].score); getchar(); }10.3.3文件的讀寫(續(xù)) if(savedata(stua,SIZE)) read_print();}/*savedata()的功能是:建立student.dat文件,將m個stua元素的數(shù)據(jù)寫入該文件中*/intsavedata(structstudentstua[],intm){ FILE*fp; if((fp=fopen("student.dat","wb"))==NULL) /*翻開磁盤文件*/ { printf("Cannotopenoutfile!\n"); return0; }10.3.3文件的讀寫(續(xù)) for(inti=0;i<m;i++) fwrite(&stua[i],sizeof(structstudent),1,fp); /*將stua[i]寫入磁盤文件*/ fclose(fp); /*關(guān)閉文件*/ return1;}/*read_print()函數(shù)功能是:將文件student.dat的內(nèi)容讀出并顯示*/voidread_print(){ structstudentstu; FILE*fp; if((fp=fopen("student.dat","rb"))==NULL) /*翻開目標(biāo)磁盤文件*/10.3.3文件的讀寫(續(xù)) { printf("Cannotopenoutfile!\n"); return; } printf("\n"); printf("nameagescoreaddr\n"); while(!feof(fp)) { fread(&stu,sizeof(structstudent),1,fp); /*讀取一個structstudent數(shù)據(jù)*/ printf("%10s%5d%5d%10s\n",,stu.age,stu.score,stu.addr); } printf("\n"); fclose(fp); /*關(guān)閉文件*/}10.3.3文件的讀寫(續(xù))運(yùn)行結(jié)果:Enterstudentname,addr,age,score:LiShanghai18,88↙Enterstudentname,addr,age,score:MingShanghai18,90↙Enterstudentname,addr,age,score:ChenShanghai19,82↙Enterstudentname,addr,age,score:QingShanghai17,92↙Enterstudentname,addr,age,score:LongShanghai17,90↙nameagescoreaddrLi1888ShanghaiMing1890ShanghaiChen1982ShanghaiQing1792ShanghaiLong1790Shanghai10.3.4去除和設(shè)置文件緩沖區(qū)(1).去除文件緩沖區(qū)函數(shù)intfflush(FILE*stream);intflushall();fflush()函數(shù)將去除由流指針stream指向的文件緩沖區(qū)里的內(nèi)容,常用于寫完一些數(shù)據(jù)后,立即用該函數(shù)去除緩沖區(qū),以免誤操作時,破壞原來的數(shù)據(jù)。flushall()將去除應(yīng)用程序翻開的所有文件所對應(yīng)的文件緩沖區(qū)。(2).設(shè)置文件緩沖區(qū)函數(shù)voidsetbuf(FILE*stream,char*buf);voidsetvbuf(FILE*stream,char*buf,inttype,unsignedsize);10.3.4去除和設(shè)置文件緩沖區(qū)(續(xù))這兩個函數(shù)將使應(yīng)用程序翻開文件后,用戶可建立自己的文件緩沖區(qū),而不使用fopen()函數(shù)翻開文件設(shè)定的默認(rèn)緩沖區(qū)。對于setbuf()函數(shù),buf指出的緩沖區(qū)的長度,由頭文件stdio.h中聲明的宏BUFSIZE的值決定,缺省值為512字節(jié)。中選定buf為空時,setbuf()函數(shù)將使的文件I/O不帶緩沖區(qū)。而對setvbuf()函數(shù),那么由malloc函數(shù)來分配緩沖區(qū)。參數(shù)size指明了緩沖區(qū)的長度(必須大于0),而參數(shù)type那么表示了緩沖的類型,其值可以取如下值:type

含義_IOFBF

文件全部緩沖,即緩沖區(qū)裝滿后,才能對文件讀寫_IOLBF

文件行緩沖,即緩沖區(qū)接收到一個換行符時,才能對文件讀寫_IONBF

文件不緩沖,此時忽略buf,size的值,直接讀寫文件,不再經(jīng)過文件緩沖區(qū)緩沖10.3.5文件的隨機(jī)讀寫函數(shù)前面介紹的是對文件的字符/字符串進(jìn)行讀寫操作,均是進(jìn)行文件的順序讀寫,即總是從文件的開頭開始進(jìn)行讀寫。這一節(jié)將講述另一種文件讀寫操作:隨機(jī)讀寫操作。C語言提供了移動文件指針和隨機(jī)讀寫的函數(shù),它們是:(1).移動文件指針函數(shù)longftell(FILE*stream);intrewind(FILE*stream);intfseek(FILE*stream,longoffset,intorigin);函數(shù)ftell()用來得到流指針stream指向的文件中,文件指針離文件開頭的偏移量。當(dāng)返回值是-1時表示出錯。rewind()函數(shù)用于流指針stream指向的文件中,文件指針移到文件的開頭,當(dāng)移動成功時,返回0,否那么返回一個非0值。fseek()函數(shù)用于流指針stream指向的文件中,把文件指針以origin為起點(diǎn)移動offset個字節(jié)的操作。其中origin指出的位置可有以下幾種:10.3.5文件的隨機(jī)讀寫函數(shù)(續(xù))

origin

數(shù)值

代表的具體位置

SEEK_SET0

文件開頭

SEEK_CUR1

文件指針當(dāng)前位置

SEEK_END

2

文件尾例如:fseek(fp,10L,0);表示把文件指針從文件開頭移到第10字節(jié)處,由于offseek()函數(shù)的第二個參數(shù)要求是長整型數(shù),故其數(shù)后帶L。fseek(fp,-15L,2);表示把文件指針從文件尾向前移動15字節(jié)。10.3.5文件的隨機(jī)讀寫函數(shù)(續(xù))例10-6向例10-5所建立的學(xué)生信息文件student.dat中追加寫入一個學(xué)生的信息,然后將文件中所有學(xué)生信息顯示在屏幕上。#include<stdio.h>structstudent{ charname[10]; intage; intscore; charaddr[30];};voidmain(){ structstudentstu; FILE*fp;10.3.5文件的隨機(jī)讀寫函數(shù)(續(xù)) /*建立學(xué)生信息:*/ printf("Enterstudent'sname,address,age,score:"); scanf("%s",); getchar(); scanf("%s",stu.addr); getchar(); scanf("%d,%d",&stu.age,&stu.score);if((fp=fopen("student.dat","ab+"))==NULL) /*"ab+"方式翻開磁盤文件*/ { printf("Cannotopenoutfile!\n"); } fwrite(&stu,sizeof(structstudent),1,fp); /*將數(shù)據(jù)追加寫入磁盤文件*/ rewind(fp);10.3.5文件的隨機(jī)讀寫函數(shù)(續(xù)) printf("nameagescoreaddress\n"); while(!feof(fp)) { fread(&stu,sizeof(structstudent),1,fp); /*讀取一個structstudent數(shù)據(jù)*/printf("%10s%5d%5d%10s\n",,stu.age,stu.score,stu.addr); } printf("\n");fclose(fp); /*關(guān)閉文件*/}10.3.5文件的隨機(jī)讀寫函數(shù)(續(xù))運(yùn)行結(jié)果:Enterstudent'sname,address,age,score:GuangShenzhen18,88↙nameagescoreaddressLi1888Shanghai↙Ming1890Shanghai↙Chen1982Shanghai↙Qing1792Shanghai↙Long1790Shanghai↙Guang1888Shenzhen↙10.3.5文件的隨機(jī)讀寫函數(shù)(續(xù))例10-7僅輸出由例10-5所建立的學(xué)生信息文件student.dat中的所有學(xué)生姓名清冊,其他信息忽略。分析:student.dat文件是由structstudent類型的數(shù)據(jù)建立的,structstudent總長度為44個字節(jié),其中name成員長度為10字節(jié),即意味著每讀完一個姓名,須由當(dāng)前位置移動34個字節(jié)才是下一個學(xué)生的姓名。#include<stdio.h>structstudent{ charname[10]; intage; intscore; charaddr[30];};10.3.5文件的隨機(jī)讀寫函數(shù)(續(xù))voidmain(){ charstu_name[10]; FILE*fp; if((fp=fopen("student.dat","ab+"))==NULL)/*"ab+"方式翻開磁盤文件*/ { printf("Cannotopenoutfile!\n"); } printf("Name:\n"); while(!feof(fp)) { fread(stu_name,10,1,fp); /*讀取一個name數(shù)據(jù)*/ printf("\t%s\n",stu_name); /*顯示*/ fseek(fp,42L,1); } printf("\n");}10.3.5文件的隨機(jī)讀寫函數(shù)(續(xù))運(yùn)行結(jié)果:Name:LiMingChenQingLongGuang10.3.6文件檢測函數(shù)C語言中常用的文件檢測函數(shù)有以下幾個:(1)文件結(jié)束檢測函數(shù)feof()函數(shù)調(diào)用格式:feof(文件指針);功能:判斷文件是否處于文件結(jié)束位置,如文件結(jié)束,那么返回值為1,否那么為0。(2)讀寫文件出錯檢測函數(shù)調(diào)用格式:ferror(文件指針);功能:檢查文件在用各種輸入、輸出函數(shù)進(jìn)行讀寫時是否出錯。如ferror()函數(shù)返回值為0表示未出錯,否那么表示有錯。(3)文件出錯標(biāo)志和文件結(jié)束標(biāo)志置0函數(shù)調(diào)用格式:clearerr(文件指針);功能:本函數(shù)用于去除出錯標(biāo)志和文件結(jié)束標(biāo)志,使它們?yōu)?值。10.3.6文件檢測函數(shù)(續(xù))例10-8從鍵盤輸入一些字符值,將它們寫入磁盤文件,當(dāng)鍵入〞#〞時結(jié)束。#include<stdio.h>voidmain(){ FILE*fp; charch,fname[10],err_flag=0; /*err_flag為讀寫磁盤文件出錯標(biāo)志*/ printf("\nEnterafilename:"); scanf("%s",fname); if((fp=fopen(fname,"w"))==NULL) /*翻開(建立)磁盤文件*/ { printf("Cannotopenoutfile!\n"); }10.3.6文件檢測函數(shù)(續(xù)) while((ch=getchar())!='#') { fputc(ch,fp); /*寫入磁盤文件*/ if(ferror(fp)) /*測試讀寫磁盤文件是否有錯*/ { err_flag=1; /*錯誤處理*/ break; } putchar(ch); } if(err_flag) printf("\nWritediskerr!\n");/*屏幕提示讀寫錯誤*/ else printf("\nO.K!\n"); /*屏幕提示讀寫正確*/}10.3.6文件檢測函數(shù)(續(xù))運(yùn)行結(jié)果:Enterafilename:mywords.c↙Ilovechina#IlovechinaO.K!10.4C庫文件C系統(tǒng)提供了豐富的系統(tǒng)文件,稱為庫文件。C的庫文件分為兩類,一類是擴(kuò)展名為".h"的文件,稱為頭文件,在前面的包含命令中我們已屢次使用過。在".h"文件中包含了常量聲明、類型聲明、宏聲明、函數(shù)原型以及各種編譯選擇設(shè)置等信息。另一類是函數(shù)庫,包括了各種函數(shù)的目標(biāo)代碼,供用戶在程序中調(diào)用。通常在程序中調(diào)用一個庫函數(shù)時,要在調(diào)用之前包含該函數(shù)原型所在的".h"文件。例10-9將例10-8所建立的磁盤文件〔mywords.c〕中的信息復(fù)制到另一個磁盤文件(youwords.c)。此例類似于DOS操作系統(tǒng)的COPY命令的功能,可以考慮在命令行輸入兩個文件名。#include<stdio.h>#include<stdlib.h>intmain(intargc,char*argv[]){10.4C庫文件(續(xù)) FILE*f_in,*f_out; /*檢查命令參數(shù)數(shù)目是否符合要求:*/ if(argc<3) { printf("Pleaseentercommandagain!\n"); exit(0); } /*翻開源磁盤文件:*/ if((f_in=fopen(argv[1],"rb"))==NULL) { printf("Cannotopeninfile!\n"); exit(0); }10.4C庫文件(續(xù)) /*翻開目的磁盤文件:*/ if((f_out=fopen(argv[2],"wb"))==NULL) { printf("Cannotopenoutfile!\n"); exit(0); } /*復(fù)制文件:*/ for(;!feof(f_in);fputc(fgetc(f_in),f_out)); fclose(f_in); fclose(f_out); printf("\nO.K!\n");10.4C庫文件(續(xù)) /*驗(yàn)證輸出:*/ for(f_out=fopen(argv[2],"r");!feof(f_out);putchar(fgetc(f_out))); printf("Isallright?\n"); return0;}假設(shè)將本例程序命名為exam.c,經(jīng)編譯后的可執(zhí)行程序?yàn)閑xam.exe,那么運(yùn)行結(jié)果:exammywords.cyouwords.cO.K!IlovechinaIsallright?10.4C庫文件(續(xù))例10-10建立一個乘法口訣的文本格式文件muti.rep,其格式為:1*1=11*2=22*2=41*3=32*3=63*3=9……1*9=92*9=18 …… 9*9=81文件建立后,再將其讀出顯示再屏幕上,進(jìn)行驗(yàn)證。#include<stdio.h>#include<stdlib.h>voidmain(){ FILE*rep; /*翻開目的磁盤文件:*/ if((rep=fopen("muti.rep","w+"))==NULL) 10.4C庫文件(續(xù)) { printf("Cannotopenoutfile!\n"); exit(0); } for(inti=1;i<10;i++) { for(intj=1;j<i+1;j++) fprintf(rep,"%d*%d=%d",i,j,i*j); fprintf(rep,"\n"); } rewind(rep); /*讀取文件,并顯示*/ for(;!feof(rep);putchar(fgetc(rep))); fclose(rep);}10.4C庫文件(續(xù))運(yùn)行結(jié)果:1*1=12*1=22*2=43*1=33*2=63*3=94*1=44*2=84*3=124*4=165*1=55*2=105*3=155*4=205*5=256*1=66*2=126*3=186*4=246*5=306*6=367*1=77*2=147*3=217*4=287*5=357*6=427*7=498*1=88*2=168*3=248*4=328*5=408*6=488*7=568*8=649*1=99*2=189*3=279*4=369*5=459*6=549*7=639*8=729*9=81習(xí)題

說明文件類型指針是一種怎樣的指針變量,它在C語言文件操作中起怎樣的作用。說明文件的翻開與關(guān)閉各有什么作用。編寫程序,實(shí)現(xiàn)從鍵盤鍵入一些字符〔以#結(jié)束〕用以建立一個磁盤文件。將題3所建立的磁盤文件的內(nèi)容顯示在屏幕上。編寫程序,實(shí)現(xiàn)從鍵盤鍵入一些字符〔以#結(jié)束〕,進(jìn)行加密后〔每個字符的ASCII碼加4〕寫入新建磁盤文件,然后從磁盤將信息讀出后復(fù)原顯示在屏幕上。習(xí)題

編寫程序,實(shí)現(xiàn)兩個磁盤文件合并〔一個文件接在另一個文件之后〕,兩個文件名通過命令行傳入〔作為main()的參數(shù)〕。有5個學(xué)生,每人有3門功課成績,從鍵盤輸入這5個學(xué)生的學(xué)號、姓名及3門功課成績,計(jì)算出每人平均成績,并用所有數(shù)據(jù)包括平均成績建立一個磁盤文件stud。將題7所建stud中5個學(xué)生數(shù)據(jù)讀出,并按平均成績排序后建立一個新文件stud_sort。用題8所建stud_sort中5個學(xué)生的姓名、平均成績兩項(xiàng)數(shù)據(jù)建立一個新文件stud_aver。第11章位運(yùn)算C語言能局部地取代匯編語言,主要原因是C語言提供了一些低級運(yùn)算符,這些運(yùn)算符能用來操作表示數(shù)據(jù)值的位序列,這些運(yùn)算符被稱為位運(yùn)算符。本章介紹C語言的位運(yùn)算符。由于位運(yùn)算只用于特殊的場合或特定的目的,所以,對于一般的讀者,只須對本章所介紹的內(nèi)容有個大致的了解即可。11.1位運(yùn)算概述所謂位運(yùn)算是指進(jìn)行二進(jìn)制位的運(yùn)算。位(bit)是計(jì)算機(jī)表示數(shù)據(jù)值的最小單位。一個位只能夠表示兩個數(shù)據(jù)值,即0或1,在物理實(shí)現(xiàn)上,0或1對應(yīng)于物理電平的高或低。位運(yùn)算不同于以前所學(xué)習(xí)的運(yùn)算,它是另一種類型的運(yùn)算。根本的運(yùn)算是將某個位置位(該位的值被置為1),或是去除某個位(該位的值被清為0)。移位運(yùn)算在一個位序列中將一個位從某個排列位置移到—個新的位置。設(shè)置或去除某個位的操作有:位與,用于去除特定的位,C語言的運(yùn)算符是&;位或,用于設(shè)置特定的位,C語言的運(yùn)算符是¦;位非,用于去除或設(shè)置特定的位,C語言的運(yùn)算符是~;位異或,用于去除或設(shè)置特定的位,C語言的運(yùn)算符是^。位與、位或和位異或運(yùn)算是二元運(yùn)算,位非運(yùn)算是一元運(yùn)算。圖11-1給出了這些運(yùn)算的說明。11.1位運(yùn)算概述(續(xù))對圖11-1概述為:位與:當(dāng)兩個操作數(shù)的值都為1時,結(jié)果值為1,否那么為0。位或:當(dāng)兩個操作數(shù)的值都為0時,結(jié)果值為0,否那么為1。位異或:當(dāng)兩個操作數(shù)的值不同為1或不同為0時,結(jié)果值為1,否那么為0。位非:當(dāng)操作數(shù)的值為1時,結(jié)果值為0,否那么,結(jié)果值為1。注意,圖11-1所示的操作對象只有一個位,它只有1或0兩個值中的一個值。但現(xiàn)代的計(jì)算機(jī)只能以字節(jié)或字為操作單位,所以,位運(yùn)算實(shí)質(zhì)上是對組成字節(jié)或字的每個位按圖11-1所示的運(yùn)算規(guī)那么進(jìn)行運(yùn)算。同樣地,在C語言中,位運(yùn)算是對整數(shù)類型的數(shù)據(jù)值進(jìn)行的運(yùn)算,但注意,這里的整數(shù)類型的數(shù)據(jù)值只是數(shù)據(jù)值在高級語言中存在的形式,位運(yùn)算越過了類型保護(hù),直接操作表示這類數(shù)據(jù)值的位。所以,在C語言中,位運(yùn)算實(shí)質(zhì)上指的是對表示整數(shù)的每個位按圖11-1所示的規(guī)那么進(jìn)行。注意,這里沒有進(jìn)位的問題存在。11.1位運(yùn)算概述(續(xù))&操作數(shù)201操作數(shù)1000101¦操作數(shù)201操作數(shù)1001111^操作數(shù)201操作數(shù)1001110~操作數(shù)01結(jié)果1011.1位運(yùn)算概述(續(xù))在C語言中,位運(yùn)算的操作數(shù)的數(shù)據(jù)類型只能是整數(shù)類型和字符類型,在表示位運(yùn)算時,對于這些數(shù)據(jù)類型的常量,我們習(xí)慣使用十六進(jìn)制表示形式,因?yàn)镃語言不支持二進(jìn)制表示形式。一個十六進(jìn)制位對應(yīng)四個二進(jìn)制位,采用十六進(jìn)制表示形式,我們可以方便地確定一個十六進(jìn)制位所表示的四個二進(jìn)制位中各個位是1或是0。表11-1給出了一個十六進(jìn)制位與四個二進(jìn)制位的對應(yīng)關(guān)系。例如:設(shè)有十六進(jìn)制數(shù)0X4B,依據(jù)表11-1,這個十六進(jìn)制數(shù)的二進(jìn)制表示為01001011。由于位運(yùn)算是對表示整數(shù)值的位進(jìn)行的操作,所以,我們必須清楚整數(shù)值在計(jì)算機(jī)上的表示,下面我們首先介紹整數(shù)值的表示,然后討論各種位運(yùn)算。11.2整數(shù)的計(jì)算機(jī)表示在C語言中,整數(shù)有八位長度、十六位長度和三十二位長度之分,在每種長度中又有有符號和無符號之別。只要我們清楚其中的一種整數(shù)的表示,并且明白在從一種長度的整數(shù)轉(zhuǎn)換為另一種長度的整數(shù)時的規(guī)那么,那么,我們也就清楚了C語言整數(shù)的表示問題。我們下面以十六位整數(shù)為例來討論整數(shù)的表示。十六進(jìn)制表示二進(jìn)制表示0X000000X100010X200100X300110X401000X501010X601100X701110X810000X910010XA10100XB10110XC11000XD11010XE11100XF111111.2整數(shù)的計(jì)算機(jī)表示(續(xù))整數(shù)的表示依賴于實(shí)現(xiàn),并且決定于程序所運(yùn)行的計(jì)算機(jī)的體系結(jié)構(gòu),我們這里討論的是在Intel80X86微處理器體系結(jié)構(gòu)制約下的整數(shù)內(nèi)部表示問題。類似于表11-1中的表示方法,在以二進(jìn)制形式表示整數(shù)值時,值的最低有效位(LSB——LeastSignificantBit)列在最右邊,稱為第0位,其它位順序從右向左排列,最左邊的位被稱為最高有效位(MSB——MostSignificantBit)。對于無符號整數(shù)(十六位,下同),最高有效位是第十五位。對于有符號整數(shù),最高有效位是第十四位,第十五位是符號位,如果符號位為1,那么表示一個負(fù)數(shù),如果符號位為0,那么表示是一個正數(shù)。11.2整數(shù)的計(jì)算機(jī)表示(續(xù))負(fù)數(shù)在表示時,除符號位之外,其余各位是由該負(fù)數(shù)的絕對值的二進(jìn)制表示的位序列求反(0求反之后為1,1求反之后為0)之后的結(jié)果值進(jìn)行二進(jìn)制加1運(yùn)算之后的位序列。下面是+7和-7的二進(jìn)制表示:+70000000000000111

符號位-71111111111111001

符號位11.2整數(shù)的計(jì)算機(jī)表示(續(xù))當(dāng)不同類型的整數(shù)參與計(jì)算時,要進(jìn)行類型轉(zhuǎn)換。當(dāng)一種長度的有符號整數(shù)轉(zhuǎn)換為相同長度的無符號整數(shù)時,表示數(shù)據(jù)值的位序列不變,而只是將符號位作為數(shù)據(jù)的有效位對待,類似地,當(dāng)將一種長度的無符號數(shù)轉(zhuǎn)換為相同長度的有符號數(shù)時,只簡單地將原數(shù)據(jù)值的最高有效位作為符號位對待。這種策略使得將一個有符號數(shù)轉(zhuǎn)換為無符號數(shù),然后再由轉(zhuǎn)換后的無符號數(shù)轉(zhuǎn)換為有符號數(shù)時,值的大小和符號不變。當(dāng)將一種長度的有符號數(shù)轉(zhuǎn)換為更長(例如三十二位)的整數(shù)時,轉(zhuǎn)換是通過復(fù)制符號位進(jìn)行的。11.2整數(shù)的計(jì)算機(jī)表示(續(xù))例如,將-7轉(zhuǎn)換為long類型的值的轉(zhuǎn)換過程為:1111111111111001

11111111111111111111111111111001這里,由于符號位為1,所以,增加的位使用1填補(bǔ)。將7轉(zhuǎn)換為long類型的值的轉(zhuǎn)換過程為:0000000000000111

00000000000000000000000000000111

這里,由于符號位為0,所以,增加的位使用0填補(bǔ)。

11.2整數(shù)的計(jì)算機(jī)表示(續(xù))這種轉(zhuǎn)換策略既不改變值的大小,也不改變符號位。如果將-7轉(zhuǎn)換為unsignedlong類型的整數(shù),那么,也是先轉(zhuǎn)換為long,再由long轉(zhuǎn)換為unsignedlong。當(dāng)將一種長度(例如十六位)的整數(shù)轉(zhuǎn)換為較短的整數(shù)時,轉(zhuǎn)換過程只是簡單地將被轉(zhuǎn)換的值的位序列截短為所需要的位數(shù),將這個位序列解釋為所需要類型的數(shù)據(jù)值。例如,將長整數(shù)的-7轉(zhuǎn)換為int類型的值的過程為:11111111111111111111111111111001

1111111111111001這種轉(zhuǎn)換在不超出結(jié)果類型的值域的情況下,總可以保持值的大小和符號位不變。在后面我們會看到,轉(zhuǎn)換過程中的符號位復(fù)制機(jī)制使我們在進(jìn)行位運(yùn)算時,有時必須考慮到程序在不同字長的計(jì)算機(jī)系統(tǒng)之間的移植性問題。11.3位運(yùn)算符說明:〔1〕位運(yùn)算符中除~外,均為二元位運(yùn)算符。〔2〕操作數(shù)只能是整型或字符型的數(shù)據(jù),不能是實(shí)型數(shù)據(jù)。下面對各個運(yùn)算符分別做介紹。運(yùn)算符含義運(yùn)算符含義&按位與~取反¦按位或<<左移^按位異或>>右移11.3.1取反運(yùn)算符~取反運(yùn)算符也稱位非運(yùn)算符,它對操作數(shù)逐位取反,值為0的位變?yōu)?,值為1的位變?yōu)?。例如:設(shè)變量bit的值為0X9A,那么表達(dá)式~bit的值為0XFF65。下面是這個運(yùn)算的示意說明。~0000000010011010

111111110110010111.3.2按位與運(yùn)算符&按位與運(yùn)算的根本用途是去除某個位或某些位。下面的例子說明了位與運(yùn)算的運(yùn)算規(guī)那么:0000100110111001&0000000010000011

0000000010000001下面給出一些常用的表達(dá)式。設(shè)有變量bit,那么表達(dá)式:bit&0XF的結(jié)果值是變量bit的值除低四位之外被清零之后的值。11.3.2按位與運(yùn)算符&(續(xù))表達(dá)式:bit&~〔0XF〕的結(jié)果值是將變量bit的值的低四位清零之后的值。注意,這個運(yùn)算不能被簡單地寫為:bit&0XFFF0這個表達(dá)式在十六位操作系統(tǒng)上可以正確工作,但在三十二位操作系統(tǒng)上就不能正確工作。下面語句判別變量bit的值的第0位或第二位是否為1:if(bit&0X5)11.3.3按位或運(yùn)算符¦按位或運(yùn)算的根本用途是設(shè)置某個位或某些位。下面的例子說明了這個運(yùn)算符的運(yùn)算規(guī)那么:0000100110111001¦0000000010000011

0000100110111011下面的表達(dá)式的結(jié)果值是將變量bit的第0位和第二位置1之后的值:bit¦0X511.3.4按位異或運(yùn)算符^按位異或運(yùn)算將兩個操作數(shù)中那些相異的位置1。下面的例子說明了這個運(yùn)算符的運(yùn)算規(guī)那么:0000100110111001^0000000010000011

0000100100111010由于在將一個位與0進(jìn)行異或操作時,結(jié)果值中對應(yīng)于該位的值保持不變,而與1進(jìn)行異或運(yùn)算時,結(jié)果值中對應(yīng)于該位的值被翻轉(zhuǎn)(0變?yōu)?,1變?yōu)?),所以,下面表達(dá)式的結(jié)果值是將變量bit的低四位翻轉(zhuǎn)之后的值:bit^0X0F11.3.5左移運(yùn)算符<<它是二元運(yùn)算符,其一般形式為:操作數(shù)1<<操作數(shù)2這個表達(dá)式的運(yùn)算結(jié)果為將“操作數(shù)1〞的值的位序列左移“操作數(shù)2〞的值所表示的次數(shù)之后的值。在左移過程中,右邊空出的位補(bǔ)0,左邊移出的位被舍棄。例如,設(shè)變量bit的值的位序列為:1101000101011111那么表達(dá)式:bit<<4的值為:0001010111110000下面的表達(dá)式的結(jié)果值為將變量bit的值的第n位置1之后的值:bit¦1<<n下面的表達(dá)式的結(jié)果值為將變量bit的值的第n位清0之后的值:bit&~(1<<n)11.3.6右移運(yùn)算符>>它是二元運(yùn)算符,其一般形式為:操作數(shù)1>>操作數(shù)2這個表達(dá)式的運(yùn)算結(jié)果為將“操作數(shù)1〞的值的位序列右移“操作數(shù)2〞的值所表示的次數(shù)之后的值。右邊移出的位被舍棄。對于無符號數(shù),左邊空出的位補(bǔ)0;對于有符號數(shù),左邊空出的位按符號位復(fù)制,也就是說,如果符號位為1(負(fù)數(shù)),空出的位補(bǔ)1,如果符號位為0,空出的位補(bǔ)0。例如,設(shè)變量bit的值的位序列為:1101000101011111對于表達(dá)式:bit>>4如果變量bit的值是無符號的,那么結(jié)果值為:0000110100010101如果變量bit的值是有符號的,那么結(jié)果值為:1111110100010101這就是說,在右移過程中,值被改變了,但符號位保持不變。11.3.7位運(yùn)算與賦值運(yùn)算的結(jié)合位運(yùn)算符與賦值運(yùn)算符可以組成復(fù)合賦值運(yùn)算符,如下:&=¦=>>=<<=^=設(shè)bit是變量,op代表符號"="左邊的運(yùn)算符,那么表達(dá)式:bitop=表達(dá)式等價為:bit=bitop(表達(dá)式)例如,設(shè)變量bit的值的位序列為:1101000101011111變量y的值的位序列為:1100000001011000那么下面的語句:bit&=y(tǒng);將變量bit的值的位序列修改為:110000000101100011.3.8位運(yùn)算符的優(yōu)先次序6個位運(yùn)算符從高到低的運(yùn)算次序如下:~高<<>>〔同級〕&^¦低除取反運(yùn)算符~外,其它運(yùn)算符的結(jié)合方向都是從左到右。11.3.9例如程序例11-1對整數(shù)a取其從右端開始的4~7位。分析:①先使a右移4位,用下面的式子實(shí)現(xiàn):a>>4②設(shè)置一個低4位全為1,其余全為0的數(shù)。用下面的式子實(shí)現(xiàn):~〔~0<<4〕(~0)的全部二進(jìn)制位為1,左移4位,這樣右端低4位為0。③將上面①、②進(jìn)行&運(yùn)算。即:〔a>>4〕&~〔~0<<4〕程序如下:#include<stdio.h>11.3.9例如程序(續(xù))voidmain(){ unsigneda,b,c,d; scanf("%o",&a); b=a>>4; c=~(~0<<4); d=b&c; printf("%o,%d\n%o,%d\n",a,a,d,d);}運(yùn)行結(jié)果如下:331↙331,21715,13輸入a的值為八進(jìn)制數(shù)331,即十進(jìn)制數(shù)217,其二進(jìn)制形式為11011001,經(jīng)運(yùn)算最后得到的d為00001101,即八進(jìn)制數(shù)15,十進(jìn)制數(shù)13。11.3.9例如程序(續(xù))例11-2循環(huán)移位。要求將a進(jìn)行右循環(huán)移位,即將a中原來左端〔16-n〕位右移n位,原來右端n位移到最左面n位。假設(shè)用16位存放一個整數(shù)。為實(shí)現(xiàn)以上目的可用如下步驟:①將a的右端n位先放到b的高n位,可用下面的語句實(shí)現(xiàn):b=a<<〔16-n〕;②將a右移n位,其左端高n位補(bǔ)0,可用下面的語句實(shí)現(xiàn):c=a>>n;③將c與b進(jìn)行按位或運(yùn)算,即:c=c¦b;程序如下:#include<stdio.h>11.3.9例如程序(續(xù))voidmain(){ unsigneda,b,c;intn; scanf("a=%o,n=%d",&a,&n);b=a<<(16-n);c=a>>n;c=c|b; printf("%o\n%o",a,c);}運(yùn)行結(jié)果如下:

1576533行開始時輸入八進(jìn)制數(shù)157653,即二進(jìn)制數(shù),循環(huán)右移3位后得二進(jìn)制數(shù),即八進(jìn)制數(shù)75765。同樣可以左循環(huán)移位。11.4位段C語言允許在一個結(jié)構(gòu)體中以位為單位來指定其成員所占內(nèi)存長度,這種以位為單位的成員稱為“位段〞或稱“位域〞。利用位段能夠用較少的位數(shù)存儲數(shù)據(jù)。位段結(jié)構(gòu)類型的一般形式為:struct<位段結(jié)構(gòu)類型名>{<成員表>};例如:structpacked_d{ unsigneda:2;unsignedb:1;unsignedc:3;unsignedd:2;unsignede:5;};11.4位段(續(xù))定義了一個位段結(jié)構(gòu)類型packed_d,包含5個位段成員,其中a占2位,b占1位,c占3位,d占2位,e占5位。至于這些位段在存儲單元中的具體存放位置,是由編譯系統(tǒng)來分配的,一般用戶不必考慮。在微機(jī)系統(tǒng)中,各位段在存儲單元中一般是從右到左順序分配的。例如,上述定義的位段結(jié)構(gòu)類型需要占2bit(16位),其存儲結(jié)構(gòu)為:15131287653210空5231211.4位段(續(xù))定義了位段結(jié)構(gòu)類型后,就可以聲明位段結(jié)構(gòu)類型的變量。例如:structpacked_dx,y;/*x,y是packed_d類型的變量*/與聲明結(jié)構(gòu)體類型變量一樣,不僅可以將位段結(jié)構(gòu)類型定義與該類型的變量聲明分開,也可以在定義位段結(jié)構(gòu)類型的同時聲明該類型的變量。例如:structpacked_d{ unsigneda:2;unsignedb:1; unsignedc:3; unsignedd:2; unsignede:5;}p1,p2;11.4位段(續(xù))還可以直接聲明位段結(jié)構(gòu)類型的變量。例如:struct{ unsigneda:2;unsignedb:1; unsignedc:3; unsignedd:2; unsignede:5;}p3;對位段結(jié)構(gòu)成員的引用方式,與引用一般結(jié)構(gòu)體成員的方式相同。例如:p3.d=3;11.4位段(續(xù))表示將3賦給位段結(jié)構(gòu)類型變量p3的位段〔成員〕d,但必須注意,在對位段進(jìn)行賦值時,要考慮到該位段所占用的二進(jìn)制位數(shù),如果所賦的數(shù)值超過了位段的表示范圍,那么自動取其低位數(shù)字。例如:p3.d=5;由于d位段只占2個二進(jìn)制位,因此,實(shí)際賦給d位段的是5的二進(jìn)制表示〔101〕的低2位,也就是1。在聲明位段和引用位段時,要注意以下幾個問題:〔1〕位段成員的的類型必須是unsigned或int類型?!?〕在位段結(jié)構(gòu)類型中,可以聲明無名位段,這種無名位段具有位段之間的分隔作用。例如:structpacked_data{ unsignedf1:2;unsignedf2:1;unsigned:2; unsignedf3:3;};11.4位段(續(xù))其中第3個位段是無名位段,占2位,在位段f2和f3之間起分隔作用。無名位段所占用的空間不起作用。如果無名位段的長度為0,那么表示下一個位段從一個新的字節(jié)開始存放。例如:structpacked_data{ unsignedf1:2;unsignedf2:1; unsigned:0; unsignedf3:3;};該位段結(jié)構(gòu)要占2bit。11.4位段(續(xù))〔3〕每個位段的長度不能大于存儲單元的長度?!?〕位段不能聲明為數(shù)組,也不能用指針指向位段。〔5〕位段結(jié)構(gòu)類型可以包含非位段成員。例如:structpacked_d1{ intn; unsignedf1:2;unsignedf2:1; unsignedf3:3;};11.4位段(續(xù))其中n為非位段成員,它單獨(dú)占2bit。非位段成員也可以在兩個位段成員之間。例如:structpacked_d1{ unsignedf1:2; intn;unsignedf2:1; unsignedf3:3;};11.4位段(續(xù))不管非位段成員在兩個位段之間,還是在兩個位段之前或之后,它總是從下一個字節(jié)開始存放,當(dāng)前字節(jié)剩下的位空間不再使用。非位段成員的引用方式與普通結(jié)構(gòu)體成員的引用方式完全相同。〔6〕位段成員可以在數(shù)值表達(dá)式中被引用,它會被自動轉(zhuǎn)換為相應(yīng)的整數(shù)。例如:p3.d+p3.b;是合法的。習(xí)題

1.在C語言中,有哪些位運(yùn)算符,其中有哪些是二元運(yùn)算符?2.假設(shè)x=3,y=2,z=1,以下各式的結(jié)果是。A.x¦y&zB.x¦y-zC.x^y&-zD.x&y&&z3.假設(shè)x=1,y=-1,以下各式的結(jié)果是。A.!x¦xB.~x¦xC.x^xD.x<<=2E.y<<24.請寫出以下程序的運(yùn)行結(jié)果。voidmain(){ chara=5,b=4; a^=b; b^=a; a^=b; printf("a=%d,b=%d\n",a,b);}習(xí)題

請寫出以下程序的運(yùn)行結(jié)果。

voidmain()

{

unsigneda=0224,b,c,d;

b=a>>4;

printf("b=%o\n",b);

c=~(~0<<4);

printf("c=%o\n",c);

d=b&c;

printf("d=%o\n",d);

}習(xí)題

編寫一函數(shù),對一個16位的二進(jìn)制數(shù)取出它的奇數(shù)位〔即從左邊起第1、3、5、…、15位〕。編寫一函數(shù)getbits,從一個16位的單元中取出某幾位〔即該幾位保存原值,其余位為0〕。函數(shù)調(diào)用形式為getbits〔value,start,end〕。其中value為該16位中的數(shù)據(jù)值,n1為欲取出的起始位,n2為欲取出的結(jié)束位。例如:getbits〔0101688,4,7〕表示對八進(jìn)制101688這個數(shù),取出它的從左面起第4位到第7位。第12章編譯預(yù)處理預(yù)處理是指在編譯前根據(jù)編譯預(yù)處理指令對源程序的一些處理工作。C源程序中參加一些預(yù)處理命令,可以提高編程效率,有利于模塊化程序設(shè)計(jì)。C語言提供的預(yù)處理功能主要有宏聲明、文件包含、條件編譯三種,分別用宏聲明命令〔#define〕、文件包含命令〔#include〕、條件編譯命令〔#ifdef—#endif等〕來實(shí)現(xiàn),這些命令以符號#開頭。預(yù)處理命令是由ANSIC統(tǒng)一規(guī)定的,它們不是語言本身的組成局部,不能直接對它們進(jìn)行編譯。必須在對程序進(jìn)行常規(guī)的編譯之前,先根據(jù)預(yù)處理命令對程序作相應(yīng)的處理,通常是進(jìn)行宏聲明的替換及包含文件的嵌入等操作。經(jīng)過預(yù)處理后程序不再包括預(yù)處理命令了,最后再由編譯程序?qū)︻A(yù)處理后的源程序進(jìn)行常規(guī)的編譯處理,得到可供執(zhí)行的目標(biāo)代碼。12.1宏聲明在C語言源程序中允許用一個標(biāo)識符來表示一個字符串,稱為“宏〞。被聲明為“宏〞的標(biāo)識符稱為“宏名〞。在編譯預(yù)處理時,對程序中所有出現(xiàn)的“宏名〞,都用宏聲明中的字符串去代換,稱為“宏展開〞。C語言用〞#define〞命令進(jìn)行宏聲明,宏聲明分為不帶參數(shù)的宏聲明和帶參數(shù)宏聲明兩種。12.1.1不帶參數(shù)的宏聲明1.不帶參數(shù)的宏聲明形式#define標(biāo)識符字符串例如:#definePI3.14……s=PI*r*r;其中,#define為宏聲明命令,PI是宏名,為了與一般的變量名或函數(shù)名區(qū)別,宏名常用大寫字母表示。在編譯預(yù)處理時,程序中該命令以后出現(xiàn)的所有標(biāo)識符PI都用"3.14"來代替。如果程序要求PI精確到小數(shù)點(diǎn)后7位,只需修改為:#definePI3.1415926采用宏聲明進(jìn)行常量置換,可以減少程序出錯,提高程序通用性。宏聲明還常用于聲明數(shù)組大?。?2.1.1不帶參數(shù)的宏聲

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論