




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
9單元結構體和文件C語言程序設計任務驅動式首先看一個例子:新生入學登記表,要求記錄每個學生的學號、姓名、性別、年齡、身份證號、家庭住址、聯(lián)系方式等信息,如表所示。問題引入QuestionIntroduction學號姓名性別年齡身份證家庭住址聯(lián)系方式11301張平W19130102201611260611河北石家莊橋西區(qū)1583211345911302李民M20130102201511260610河北石家莊裕華……………………………可以用前面章節(jié)學過的數組來解決此問題,在此問題中,因為要有很多學生的信息要處理,按照前面學習過的知識,數組是由相同類型的數據構成的,所以可以使用7個單獨的數組(學號數組no、姓名數組name、性別數組sex、年齡數組age、身份證號數組pno、問題引入QuestionIntroduction家庭住址數組addr、聯(lián)系方式數組tel)分別保存這幾類信息。雖然分別設立的幾個數組將給數據處理造成麻煩,但很多計算機語言只能這樣處理(如早期的FORTRAN、PASCAL、BASIC)。這時,可以用C語言提供的結構體數據類型來處理此問題。另外,在前面各單元進行數據處理時,無論數據量有多大,每次運行程序都必須通過鍵盤輸入,程序處理的結果也只能輸出到屏幕上,如果將輸入或輸出的數據以磁盤文件的形式存儲起來,則在進行大批量數據處理時會十分方便。本單元通過兩個任務完成復雜數據的組織和存儲。目錄導航任務1存儲聯(lián)系人信息—結構體的應用任務2實現小型通訊錄—文件的運用思考與訓練單元小結任務1存儲聯(lián)系人信息—結構體的應用現在是一個信息化的時代,辦公室主任小孫為了工作方便,計劃用C語言編寫一個程序,實現本部門人員聯(lián)系信息的存儲和輸出,聯(lián)系人信息如表9-2所示。工作任務學號姓名性別年齡身份證家庭住址聯(lián)系方式11301張平W19130102201611260611河北石家莊橋西區(qū)1583211345911302李民M20130102201511260610河北石家莊裕華……………………………任務1存儲聯(lián)系人信息—結構體的應用思路指導C語言利用結構體將同一個對象的不同類型數據,組成一個有聯(lián)系的整體。也就是說,可以定義一種結構體類型將屬于同一個對象的不同類型的數據組合在一起。結構體是一種自定義數據類型。在本任務中,需要存儲、輸出多個聯(lián)系人(對象)的信息,可以使用數組元素為結構體類型的數組,其中每個元素是一個聯(lián)系人(對象)的相關的整體信息。相關知識(一)定義結構體類型struct結構體名{
類型1成員1;
類型2成員2;…
類型n成員n;};1.定義結構類型的一般形式(一)定義結構體類型2.幾點說明(1)結構體名:結構體類型的名稱,遵循標識符命名規(guī)則。(2)結構體有若干數據成員,分別屬于各自的數據類型,結構體成員名同樣遵循標識符規(guī)定,名稱可以與程序中其他變量或標識符相同。(3)使用結構體類型時,“struct結構體名”作為一個整體,表示名為“結構體名”的結構體類型。(4)結構體類型的成員可以是基本數據類型,也可以是其他的已經定義的結構體類型。學生信息的結構體類型定義如例9.1所示。(一)定義結構體類型例9.1結構類型定義示例structstudent{intno;charname[20];charsex;intage;charpno[19];charaddr[80];chartel[12];};structstudent是結構體類型名,struct是關鍵詞,在定義和使用時均不能省略。該結構體類型由7個成員組成,分別屬于不同的數據類型。(二)定義和初始化結構體變量1.定義結構類型變量(1)先定義結構體類型,再定義結構體變量。結構體類型定義(前面已經介紹過);結構體變量定義;其中,結構體變量定義的格式為struct結構體類型名結構體變量名;例如:structstudent{intno;charname[20];charsex;charpno[19];charaddr[80];chartel[12];};/*定義結構體類型structstudent*/structstudentstudent1,student2;/*定義2個類型為structstudent*/的結構體變量student1,student2*/(二)定義和初始化結構體變量(2)在定義結構體類型的同時定義結構體變量。struct結構體名{…結構成員…}結構體變量名表;例如:structstudent{intno;charname[20];charsex;intage;charpno[19];charaddr[80];chartel[12];}student1,student2;這是一種緊湊的格式,既定義類型,也定義變量,如果需要,在程序中還可以使用定義的結構體類型,定義其他同類型變量。(二)定義和初始化結構體變量(3)直接定義結構體變量(不給出結構體類型名,即匿名的結構體類型)。struct{…結構成員…}結構體變量名表;例如:structstudent{intno;charname[20];charsex;intage;charpno[19];charaddr[80];chartel[12];}student1,student2;結構體類型與結構類型變量是兩個不同的概念,在定義時一般先定義一個結構體類型,然后定義變量為該類型;賦值、存取或運算只能對變量,不能對類型;編譯時只對變量分配空間,對類型不分配空間。(二)定義和初始化結構體變量2.結構體變量的初始化結構體類型的變量存儲類型可分為自動型、靜態(tài)型和外部類型,但是沒有寄存器類型的結構體類型變量。結構體變量初始的格式如下。struct結構體名{
類型1成員1;
類型2成員2;……
類型n成員n;}變量名={初始化數據};(二)定義和初始化結構體變量#include<stdio.h>voidmain(){structstu{intnum;char*name;charsex;floatscore;}boy2,boy1={102,"zhangping",'M',78.5};boy2=boy1;
printf({"number=%d\nname=%s\n",boy2.num,};printf({"sex=%c\nscorce=%f\n",boy2.sex,boy2.scorce};}例9.2對結構類型變量的初始化(三)結構體變量的引用結構體變量引用的基本格式為結構體變量名.結構成員名其中,“.”運算符是結構成員引用運算符。例如:student1.num=11301;scanf("%s",);student1.age++;(四)結構體數組數組元素類型為結構體類型的數組稱為結構體數組,C語言允許使用結構體數組存放對象的數據。結構體數組的定義只是將“變量名”用“數組名[長度]”代替,有以下3種方式。struct結構體名{…};struct結構體名結構體數組名[數組的長度];(1)先定義結構體類型,然后定義結構體數組。struct結構體名{…}結構體數組名[數組的長度];(2)定義結構體類型的同時定義結構體數組。struct{…}結構體數組名[數組的長度];(3)匿名結構體數組定義。(四)結構體數組structstudent{intno;charname[20];charsex;intage;charpno[19];charaddr[40];chartel[20];}stu[30];定義結構體數組后,可以采用:“數組元素.成員名”方式引用結構體數組中的某個數組元素。例9.3定義30個元素的結構體數組stu,其中每個元素都是structstudent類型任務1存儲聯(lián)系人信息—結構體的應用任務實施1.流程圖流程圖如圖所示。2.程序代碼#include<stdio.h>voidmain(){structbirthday/*出生日期的定義*/{intyear;intmonth;intday;
};structworker/*職工信息的定義*/{charname[20];charsex;structbirthdaydate;charpno[19];charaddr[80];chartel[12];}zg[100];inti;printf("請輸入職工信息:");任務1存儲聯(lián)系人信息—結構體的應用for(i=0;i<100;i++){scanf("%s",zg[i].name);scanf("%c",zg[i].sex);scanf("%d",zg[i].birthday.year);scanf("%d",zg[i].birthday.month);scanf("%d",zg[i].birthday.day);scanf("%s",zg[i].pno);scanf("%s",zg[i].tel);}printf("姓名性別出生日期身份證號聯(lián)系方式\n"for(i=1;i<100;i++){printf("%s",zg[i].name);printf("%c",zg[i].sex);printf("%d",zg[i].birthday.year);printf("%d",zg[i].birthday.month);printf("%d",zg[i].birthday.day);printf("%s",zg[i].pno);printf("%s\n",zg[i].tel);}運行結果如圖所示。任務1存儲聯(lián)系人信息—結構體的應用特別提示(1)結構成員本身又是結構體類型時的子成員的訪問—使用成員運算符逐級訪問。(2)同一種類型的結構體變量之間可以直接賦值(整體賦值,成員逐個賦值)。(3)不允許將一個結構體變量整體輸入/輸出。(4)在對結構體數組初始化時,要將每個元素的數據用“{}”括起來。例如:student1.birthday.yearstudent1.birthday.monthstudent1.birthday.day例如:student2=student1;目錄導航任務1存儲聯(lián)系人信息—結構體的應用任務2實現小型通訊錄—文件的運用思考與訓練單元小結任務2實現小型通訊錄—文件的運用為了方便管理,班主任小王計劃建立2014信息管理班通訊錄,他想到本學期該班的學生正好學習了C語言程序設計課程,于是,他安排學習委員張雪利用C語言的文件操作設計開發(fā)一個小型的通訊錄管理系統(tǒng),該系統(tǒng)至少具有如下功能。(1)通訊錄內的人員信息至少包括學號、姓名、地址、電話號碼。(2)顯示所有人員的信息。(3)通過輸入姓名查找人員信息。(4)通過輸入姓名查找到要刪除的人員信息,然后可以刪除。(5)通過輸入姓名查找到要修改的人員信息,然后可以修改。(6)添加人員信息。工作任務任務2實現小型通訊錄—文件的運用思路指導根據要求,通訊錄數據以文本文件的形式存放在文件中,故需要提供文件的輸入、輸出等操作;還需要保存記錄以進行修改、刪除、查找等操作;另外還應提供鍵盤式選擇菜單實現功能選擇;可以根據要求添加用戶想添加的人員信息。相關知識(一)初識文件1.區(qū)別不同的文件(1)文本文件和二進制文件。①文本文件(文本數據流)。一個文本數據流是一行行的字符,每一個字符以其ASCII形式存放,每一個字符占一字節(jié)。文本文件的優(yōu)點是可以閱讀和打印,但是計算機進行數據處理時需要轉換為二進制數的形式。②二進制文件。將內存中的數據按照其在內存中的存儲形式原樣輸出,并保存在文件中。二進制文件占用空間少,內存數據和磁盤數據交換時無須轉換,但是二進制文件不可閱讀、打印。(一)初識文件(2)緩沖文件系統(tǒng)和非緩沖文件系統(tǒng)。系統(tǒng)自動在內存中為每個正在使用的文件開辟一個緩沖區(qū)。從磁盤讀數據時,一次從磁盤文件將一些數據輸入內存緩沖區(qū)(充滿緩沖區(qū)),然后再從緩沖區(qū)逐個將數據送給接收變量;向磁盤文件輸出數據時,先將數據送到內存緩沖區(qū),裝滿緩沖區(qū)后才一起輸出到磁盤。這樣就減少對磁盤的實際訪問(讀/寫)次數,從而增加了程序執(zhí)行的速度,但是占用了一塊內存空間。此外,如果沒有及時關閉文件會造成數據丟失。②非緩沖文件系統(tǒng)。①緩沖文件系統(tǒng)。數據存放時直接通過磁盤,并不會將數據存放到一個較大的內存空間中。由于篇幅有限,本書不予介紹。(一)初識文件(3)順序存取文件和隨機存取文件。可以通過調用C語言庫函數指定開始讀(或寫)的字節(jié)號,然后進行讀(或寫)。利用隨機存儲的方式查找數據時,通常會用一些公式來計算指針要指向哪一條數據,找到符合條件的數據后,再對該數據進行存取操作。每當“打開”這類文件進行讀寫操作時,總是從文件的開始,從頭到尾順序讀寫。所以,當數據量非常龐大時,順序存取方式相當緩慢。順序存取文件隨機(直接)存取文件(一)初識文件2.操作文件的基本方法和步驟打開文件:Step11Step22讀寫數據:Step33關閉文件:程序在打開文件時,首先在內存中為輸入、輸出數據開辟緩沖區(qū);向數據文件中寫數據時,先將數據送入輸出文件緩沖區(qū)中,當輸出文件緩沖區(qū)寫滿時,再一起寫到外存上;從數據文件中讀取數據也是這樣,只不過順序相反。如果緩沖區(qū)不滿時結束操作,文件中的數據就會丟失;但如果關閉文件,不管緩沖區(qū)是否已經寫滿,都會把緩沖區(qū)的數據寫入外存中,使數據不會丟失。不打開文件就無法讀寫文件中的數據,不關閉文件就會浪費操作系統(tǒng)資源,并可能導致數據丟失。所以,在對文件的操作結束后,一定要及時關閉文件。(一)初識文件3.指向文件—文件類型指針(1)文件類型(結構體)—FILE類型。FILE類型是一種結構體類型,在這個結構體中包含了緩沖區(qū)的大小、文件狀態(tài)標志、文件描述符等信息。該結構類型在C語言系統(tǒng)中預先定義,包含在頭文件"stdio.h"中。程序使用一個文件,系統(tǒng)就為此文件開辟一個FILE類型變量,程序使用幾個文件,系統(tǒng)就開辟幾個FILE類型變量,存放各個文件的相關信息。(2)文件指針。通常對FILE結構體的訪問是通過FILE類型指針變量(簡稱文件指針)完成的,文件指針變量指向文件類型變量,簡單地說,文件指針指向文件。執(zhí)行時,系統(tǒng)開辟一個文件變量,并返回文件指針,將此指針賦值(保存)給文件指針變量fp??梢杂谜Z句fclose(fp);關閉文件,釋放文件指針fp指向的文件變量。(一)初識文件4.打開與關閉文件(1)文件的打開(fopen函數)。調用fopen的格式如下。FILE*fp;注意:一定將函數返回的文件指針賦值給“文件指針變量”。例如:FILE*fp;fp=fopen("d:\\a1.txt","r");說明①打開d:盤根目錄下文件名為a1.txt的文件,打開方式“r”表示只讀。②fopen函數返回指向d:\a1.txt的文件指針,然后賦值給fp,fp指向此文件,即fp與此文件關聯(lián)。③關于文件名要注意:文件名包含“文件名.擴展名”;路徑要用“\\”表示。(一)初識文件(2)關于打開方式。①文件打開一定要檢查fopen函數的返回值,因為有可能文件不能正常打開。不能正常打開時fopen函數返回NULL??梢杂孟旅娴男问綑z查。if((fp=fopen(…))==NULL){printf("erroropenfile\n");exit(1);}②“r”方式。只能從文件讀入數據而不能向文件寫入數據。該方式要求欲打開的文件已經存在。③“w”方式。只能向文件寫入數據而不能從文件讀入數據。如果文件不存在,創(chuàng)建文件,如果文件存在,原來的文件被刪除,然后重新創(chuàng)建文件(相當覆蓋原來的文件)。④“a”方式。在文件末尾添加數據,而不刪除原來的文件。該方式要求欲打開的文件已經存在。⑤“+”(“r+,w+,a+”)。均為可讀、可寫。但是“r+”,“a+”要求文件已經存在,“w+”無此要求;“r+”打開文件時文件指針指向文件開頭,“a+”打開文件時,文件指針指向文件末尾。(一)初識文件(3)文件的關閉(fclose函數)。⑥“b、t”。以二進制或文本方式打開文件,默認是文本方式,t可以省略。讀文本文件時,將“回車/換行”轉換為一個“換行”;寫文本文件時,將“換行”轉換為“回車/換行”。⑦程序開始運行時,系統(tǒng)自動打開3個標準文件:標準輸入、標準輸出、標準出錯輸出。一般這3個文件對應于終端(鍵盤、顯示器)。這3個文件不需要手工打開就可以使用。標準輸入、標準輸出、標準出錯輸出對應的文件指針分別是stdin、stdout、stderr。文件使用完畢后必須關閉,以避免數據丟失。格式:fclose(文件指針);(二)讀寫文本文件(ASCII文件)(1)fputc()函數—寫一個字符到磁盤文件。格式:fputc(ch,fp)功能:將字符ch寫入fp指向的文件。返回:輸出成功返回值—輸出的字符ch;輸出失敗—返回EOF(?1)。其他說明:每次寫入一個字符,文件位置指針自動指向下一字節(jié)。1.文件的字符輸入輸出函數(二)讀寫文本文件(ASCII文件)例9.4從鍵盤輸入一行字符,寫入文本文件string.txt中#include<stdio.h>voidmain(){FILE*fp;charch;if((fp=fopen("string.txt","w"))==NULL)/*打開文件string.txt(寫)*/{
printf("can'topenfile\n");exit(1);
}do/*不斷從鍵盤讀字符并寫入文件,直到遇到換行符*/{ch=getchar();/*從鍵盤讀取字符*/fputc(ch,fp);/*將字符寫入文件*/}while(ch!='\n');fclose(fp);/*關閉文件*/}(二)讀寫文本文件(ASCII文件)(2)fgetc()函數—從磁盤文件讀一個字符。格式:ch=fgetc(fp);功能:從fp指向的文件讀一個字符,字符由函數返回。返回的字符可以賦值給ch,也可以直接參與表達式運算。返回:輸入成功返回輸入的字符;遇到文件結束返回EOF(?1)。說明①每次讀入一個字符,文件位置指針自動指向下一字節(jié)。②因為文本文件的內部全部是ASCII字符,其值不可能是EOF(-1),所以可以使用EOF(-1)確定文件結束;但是對于二進制文件不能這樣做,因為可能在文件中某字節(jié)的值恰好等于-1,此時使用-1判斷文件結束是不恰當的。為了解決這個問題,ANSIC提供了feof(fp)函數用于判斷文件是否真正結束。(二)讀寫文本文件(ASCII文件)(1)fputc()函數—寫一個字符到磁盤文件。Feof()函數—測試文件是否結束。格式:feof(fp);功能:在程序中判斷被讀文件是否已經讀完,feof函數適合用于判斷文本文件和二進制文件是否結束。返回:當遇到結束標志時,函數返回值是1,否則返回值為0。2.測試文件結束函數feof()(二)讀寫文本文件(ASCII文件)例9.5將磁盤上一個文本文件的內容復制到另一個文件中#include<stdio.h>main(){FILE*fp_in,*fp_out;charinfile[20],outfile[20];printf("Entertheinfilename:");/*輸入欲復制的源文件的文件名*/scanf("%s",infile);
printf("Entertheoutfilename:");/*輸入復制的目標文件的文件名*/scanf("%s",outfile);if((fp_in=fopen(infile,"r"))==NULL)
/*打開源文件*/{
printf("can'topenfile:%s",infile);exit(1);}if((fp_out=fopen(outfile,"w"))==NULL)/*打開目標文件*/{printf("can'topenfile:%s",outfile);exit(1);}while(!feof(fp_in))/*若源文件未結束*/
{/*從源文件讀一個字符,寫入目標文件*/fputc(fgetc(fp_in),fp_out);}fclose(fp_in);/*關閉源、目標文件*/fclose(fp_out);}(二)讀寫文本文件(ASCII文件)(1)fgets()函數—從磁盤文件讀一個字符串。格式:fgets(字符串指針變量str,字符串長度n,文件指針變量fp)。功能:從fp指向的文件讀n?1個字符,并將這些字符放到以str為起始地址的單元中。如果在讀入n?1個字符結束前遇到換行符或EOF,讀入結束。字符串讀入后,在該字符串最后加一個'\0'字符。返回:輸入成功時,返回輸入串的首地址;遇到文件結束或出錯時,返回NULL。3.文件的字符串輸入輸出函數(二)讀寫文本文件(ASCII文件)例9.6編制一個將文本文件中的全部信息顯示到屏幕的程序#include<stdio.h>voidmain(intargc,char*argv[]){FILE*fp;charstring[81];/*最多保存80個字符,外加一個字符串結束標志*/
if(argc!=2||(fp=fopen(argv[1],"r"))==NULL){ /*打開文件*/
printf("can'topenfile");exit(1);}while(fgets(string,81,fp)!=NULL)/*如果未讀到文件末尾(EOF),函數不會返回NULL,繼續(xù)循環(huán)(執(zhí)行循環(huán)體)*//*從文件一次讀80個字符,遇換行或EOF,提前帶回字符串*/printf("%s",string);/*打印串*/
fclose(fp);/*關閉文件*/}(二)讀寫文本文件(ASCII文件)(2)fputs()函數—寫一個字符串到磁盤文件。格式:fputs(字符串str,文件指針變量fp)。功能:向fp指向的文件寫入以str為首地址的字符串。返回:輸入成功時,返回0;出錯時,返回非0值。例9.7在文本文件string.txt末尾添加若干行字符#include<stdio.h>voidmain(){FILE*fp;chars[81];
if((fp=fopen("string.txt","a"))==NULL)/*打開文件*/{printf("can'topenfile\n");exit(1);
}while(strlen(gets(s))>0)/*從鍵盤讀入一個字符串,遇到空行(strlen=0)結束*/{fputs(s,fp);/*將字符串寫進文件*/fputs("\n",fp);/*補一個換行符*/}fclose(fp);/*關閉文件*/}(二)讀寫文本文件(ASCII文件)格式化文件讀寫函數fprintf、fscanf與函數printf、scanf的作用基本相同,區(qū)別在于fprintf、fscanf讀寫的對象是磁盤文件,printf、scanf讀寫的對象是終端。格式:fprintf(fp,格式字符串,輸出表列);fscanf(fp,格式字符串,輸入表列);其中,fp是文件指針。例如:fprintf(fp,"%d,%f",i,j);將整型變量i和實型變量j的值按照%d和%f的格式輸出到fp指向的文件中。fscanf(fp,"%d%f",i,j);從fp指向的文件中讀取一個整型數據賦值給變量i,一個實型數據賦值給變量j。4.文件的格式輸入輸出函數(三)讀寫二進制文件從文件(特別是二進制文件)讀寫一塊數據(如一個數組元素、一個結構體變量的數據—記錄)時,使用數據塊讀寫函數非常方便。數據塊讀寫函數的調用形式為intfread(void*buffer,intsize,intcount,FILE*fp);intfwrite(void*buffer,intsize,intcount,FILE*fp);4.文件的格式輸入輸出函數其中:(1)buffer是指針,對fread是用于存放讀入數據的首地址;對fwrite是要輸出數據的首地址。(2)size是一個數據塊的字節(jié)數(每塊大?。琧ount是要讀寫的數據塊塊數。(3)fp是文件指針。(4)fread、fwrite返回讀取/寫入的數據塊塊數(正常情況=count)。(5)以數據塊方式讀寫,文件通常以二進制方式打開。(三)讀寫二進制文件例9.8從鍵盤輸入一批學生的數據,然后把它們轉存到磁盤文件stud.dat中#include<stdio.h>#include<stdlib.h>#include<ctype.h>structstudent{intnum;charname[20];charsex;intage;floatscore;};/*共5個成員,占用29bytes*/voidmain(){structstudentstud;charnumstr[20],ch;/*numstr為臨時字符串,保存學號/年齡/成績,然后轉換為相應類型;ch為Y/N*/FILE*fp;if((fp=fopen("stud.dat","wb"))==NULL)/*以二進制、寫方式打開文件*/{
printf("can'topenfilestud.dat\n");exit(1);}(三)讀寫二進制文件do{printf("enternumber:");gets(numstr);stud.num=atoi(numstr);printf("entername:");gets();printf("entersex:");stud.sex=getchar();getchar();printf("enterage:");gets(numstr);stud.age=atoi(numstr);printf("enterscore:");gets(numstr);stud.score=atof(numstr);/*每次將一個準備好的結構體變量的所有內容寫入文件(寫一個記錄)*/fwrite(&stud,sizeof(structstudent),1,fp);printf("haveanotherstudentrecord(y/n)?");ch=getchar();getchar();}while(toupper(ch)=='Y');/*循環(huán)讀數據/寫記錄*/
fclose(fp);/*關閉文件*/任務2實現小型通訊錄—文件的運用任務實施1.流程圖流程圖如圖所示。2.程序代碼#include<stdio.h>#include<stdlib.h>#include<string.h>#defineBUFLEN100#defineLEN15#defineN100structrecord/*結構體*/{charcode[LEN+1];/*學號*/charname[LEN+1];/*姓名*/intage;/*年齡*/charsex[3];/*性別*/chartime[LEN+1];/*出生年月*/charadd[30];/*家庭地址*/chartel[LEN+1];/*電話號碼*/charmail[30];/*電子郵件地址*/}stu[N];任務2實現小型通訊錄—文件的運用intk=1,n,m;/*定義全局變量*/voidreadfile();/*函數聲明*/voidseek();voidmodify();voidinsert();voiddel();voiddisplay();voidsave();voidmenu();intmain(){while(k)menu();system("pause");return0;}voidreadfile()/*建立信息*/{char*p="student.txt";FILE*fp;inti=0;if((fp=fopen("student.txt","r"))==NULL){printf("Openfile%serror!Strikeanykeytoexit!",p);system("pause");exit(0);}while(fscanf(fp,"%s%s%d%s%s%s%s%s",stu[i].code,stu[i].name,&stu[i].age,stu[i].sex,stu[i].time,stu[i].add,stu[i].tel,stu[i].mail)==8){i++;i=i;}任務2實現小型通訊錄—文件的運用fclose(fp);n=i;printf("錄入完畢!\n");}voidseek(){
/*查找*/inti,item,flag;chars1[21];/*以姓名和學號最長長度+1為準*/printf("------------------\n");printf("-----1.按學號查詢-----\n");printf("-----2.按姓名查詢-----\n");printf("-----3.退出本菜單-----\n");printf("------------------\n");while(1){printf("請選擇子菜單編號:");scanf("%d",&item);flag=0;switch(item){case1:printf("請輸入要查詢的學生的學號:\n");scanf("%s",s1);for(i=0;i<n;i++)if(strcmp(stu[i].code,s1)==0)
{flag=1;printf("學號姓名年齡性別出生年月地址電話E-mail\n");printf("---------------------------------------------------------\n");printf("%6s%7s%6d%5s%9s%8s%10s%14s\n",stu[i].code,stu[i].name,stu[i].age,stu[i].sex,stu[i].time,stu[i].add,stu[i].tel,stu[i].mail);}if(flag==0)printf("該學號不存在!\n");break;任務2實現小型通訊錄—文件的運用case2:printf("請輸入要查詢的學生的姓名:\n");scanf("%s",s1);for(i=0;i<n;i++)if(strcmp(stu[i].name,s1)==0)
{flag=1;printf("學號姓名年齡性別出生年月地址電話E-mail\n");printf("--------------------------------------------------------------------\n");printf("%6s%7s%6d%5s%9s%8s%10s%14s\n",stu[i].code,stu[i].name,stu[i].age,stu[i].sex,stu[i].time,stu[i].add,stu[i].tel,stu[i].mail);}if(flag==0)printf("該姓名不存在!\n");break;case3:return;default:printf("請在1~3之間選擇\n");}}}voidmodify(){/*修改信息*/inti,item,num;/*以姓名和學號最長長度+1為準*/charsex1[3],s1[LEN+1],s2[LEN+1];printf("請輸入要修改的學生的學號:\n");
scanf("%s",s1);for(i=0;i<n;i++)/*比較字符串是否相等*/if(strcmp(stu[i].code,s1)==0)num=i;printf("------------------\n");printf("1.修改姓名\n");printf("2.修改年齡\n");printf("3.修改性別\n");printf("4.修改出生年月\n");printf("5.修改地址\n");printf("6.修改電話號碼\n");任務2實現小型通訊錄—文件的運用printf("7.修改E-mail地址\n");printf("8.退出本菜單\n");printf("------------------\n");while(1){ printf("請選擇子菜單編號:");scanf("%d",&item);switch(item) {case1:printf("請輸入新的姓名:\n");scanf("%s",s2);strcpy(stu[num].name,s2);break;case2:printf("請輸入新的年齡:\n");scanf("%d",&stu[num].age);break;case3:printf("請輸入新的性別:\n");scanf("%s",sex1);strcpy(stu[num].sex,sex1);break;case4:printf("請輸入新的出生年月:\n");scanf("%s",s2);strcpy(stu[num].time,s2);break;case5:printf("請輸入新的地址:\n");scanf("%s",s2);strcpy(stu[num].add,s2);break;case6:printf("請輸入新的電話號碼:\n");scanf("%s",s2);strcpy(stu[num].tel,s2);break;任務2實現小型通訊錄—文件的運用case7:printf("請輸入新的E-mail地址:\n");scanf("%s",s2);strcpy(stu[num].mail,s2);break;case8:return;default:printf("請在1~8之間選擇\n");} }}voidsort()/*按學號排序*/{inti,j,*p,*q,s;chartemp[10];for(i=0;i<n-1;i++){for(j=n-1;j>i;j--)if(strcmp(stu[j-1].code,stu[j].code)>0){strcpy(temp,stu[j-1].code);strcpy(stu[j-1].code,stu[j].code);strcpy(stu[j].code,temp);strcpy(temp,stu[j-1].name);strcpy(stu[j-1].name,stu[j].name);strcpy(stu[j].name,temp);strcpy(temp,stu[j-1].sex);strcpy(stu[j-1].sex,stu[j].sex);strcpy(stu[j].sex,temp);strcpy(temp,stu[j-1].time);strcpy(stu[j-1].time,stu[j].time);strcpy(stu[j].time,temp);strcpy(temp,stu[j-1].add);strcpy(stu[j-1].add,stu[j].add);strcpy(stu[j].add,temp);strcpy(temp,stu[j-1].tel);任務2實現小型通訊錄—文件的運用strcpy(stu[j-1].tel,stu[j].tel);strcpy(stu[j].tel,temp);strcpy(temp,stu[j-1].mail);strcpy(stu[j-1].mail,stu[j].mail);strcpy(stu[j].mail,temp);p=&stu[j-1].age;q=&stu[j].age;s=*q;*q=*p;*p=s;}
}}voidinsert(){/*插入函數*/inti=n,j,flag;printf("請輸入待增加的學生數:\n");scanf("%d",&m);do{flag=1;while(flag){flag=0;printf("請輸入第%d個學生的學號:\n",i+1);scanf("%s",stu[i].code);for(j=0;j<i;j++)if(strcmp(stu[i].code,stu[j].code)==0){printf("已有該學號請檢查后重新錄入!\n");flag=1;break;/*如有重復立即退出該層循環(huán),提高判斷速度*/}}printf("請輸入第%d個學生的姓名:\n",i+1);scanf("%s",stu[i].name);printf("請輸入第%d個學生的年齡:\n",i+1);任務2實現小型通訊錄—文件的運用scanf("%d",&stu[i].age);printf("請輸入第%d個學生的性別:\n",i+1);scanf("%s",stu[i].sex);printf("請輸入第%d個學生的出生年月:(格式:年.月)\n",i+1);scanf("%s",stu[i].time);printf("請輸入第%d個學生的地址:\n",i+1);scanf("%s",stu[i].add);printf("請輸入第%d個學生的電話:\n",i+1);scanf("%s",stu[i].tel);printf("請輸入第%d個學生的E-mail:\n",i+1);scanf("%s",stu[i].mail);if(flag==0){i=i;i++;}}while(i<n+m);n+=m;printf("錄入完畢!\n\n");sort();}voiddel(){inti,j,flag=0;chars1[LEN+1];printf("請輸入要刪除學生的學號:\n");scanf("%s",s1);for(i=0;i<n;i++)if(strcmp(stu[i].code,s1)==0){flag=1;for(j=i;j<n-1;j++)stu[j]=stu[j+1];}任務2實現小型通訊錄—文件的運用if(flag==0)printf("該學號不存在!\n");if(flag==1){printf("刪除成功!顯示結果請選擇菜單6\n");n--;}}voiddisplay(){inti;printf("所有學生的信息為:\n");printf("學號姓名年齡性別出生年月地址電話E-mail\n");printf("---------------------------------------------------------\n");for(i=0;i<n;i++){printf("%6s%7s%5d%5s%9s%8s%10s%14s\n",stu[i].code,stu[i].name,stu[i].age,stu[i].sex,stu[i].time,stu[i].add,stu[i].tel,stu[i].mail);}}voidsave(){inti;FILE*fp;fp=fopen("student.txt","w");/*寫入*/for(i=0;i<n;i++){fprintf(fp,"%s%s%d%s%s%s%s%s\n",stu[i].code,stu[i].name,stu[i].age,stu[i].sex,stu[i].time,stu[i].add,stu[i].tel,stu[i].mail);}任務2實現小型通訊錄—文件的運用fclose(fp);}voidmenu()/*界面*/{intnum;printf("\n\n計算機技術系信息管理班通訊錄管理系統(tǒng)\n\n");printf("**********************制作人張雪*************************\n\n");printf("*********************系統(tǒng)功能菜單************************\n");printf("-------------------------------------------------------\n");printf("1.刷新學生信息2.查詢學生信息\n");printf("3.修改學生信息4.增加學生信息\n");printf("5.按學號刪除信息6.顯示當前信息\n");printf("7.保存當前學生信息8.退出系統(tǒng)\n");printf("--------------------------------------------------------\n");printf("請選擇菜單編號:");scanf("%d",&num);任務2實現小型通訊錄—文件的運用switch(num){case2:seek();break;case3:modify();break;case4:insert();break;case5:del();break;case6:display();break;case7:save();break;case8:k=0;break;default:printf("請在1~8之間選擇\n");}}程序運行界面如圖所示。任務2實現小型通訊錄—文件的運用特別提示(1)文件空讀:在輸入字符并按回車后,實際緩沖中有兩個字符(如'f/m'和'\n'),如果僅需要前面有意義的字符('f/m'),可以用“空讀”略過'\n'。(2)什么情況要空讀?如果后面的讀取鍵盤操作是讀取數字(整數/浮點數),就不必空讀;如果后面的讀取鍵盤是讀字符或字符串,則應當“空讀”。(3)C語言即使寫文本文件、關閉時,也不自動加文件結束符。拓展與提高(一)結構體指針變量1.結構體指針變量的定義struct結構體名*結構體指針變量名;例如,structstudent*p;定義了一個結構體指針變量,它可以指向一個structstudent結構體類型的數據。2.通過結構體指針變量訪問結構體變量的成員(兩種訪問形式)(1)(*結構體指針變量名).成員名。*結構體指針變量名=指向的結構體變量名,注意:“.”運算符的優(yōu)先級比“*”運算符高。(2)結構體指針變量名->成員名。其中“->”是指向成員運算符的。例如,可以使用(*p).age或p->age,作用就是訪問p指向的結構體的age成員。(一)結構體指針變量例9.9用指針訪問結構體變量及結構體數組#include<stdio.h>voidmain(){structstudent/*結構體類型定義*/{
intnum;charname[20];charsex;intage;floatscore;};
/*結構體數組stu、結構體變量student1的定義和初始化*/
structstudentstu[3]={{11302,"Wang",'F',20,483},{11303,"Liu",'M',19,503},{11304,"Song",'M',19,471.5}};structstudentstudent1={11301,"Zhang",'F',19,496.5},*p,*q;inti;/*p指向結構體變量*/
p=&student1;printf("%s,%c,%5.1f\n",,(*p).sex,p->score);/*訪問結構體變量*/
q=stu;/*q指向結構體數組的元素*/
for(i=0;i<3;i++,q++)/*循環(huán)訪問結構體數組的元素(下標變量)*/printf("%s,%c,%5.1f\n",q->name,q->sex,q->score);}(二)結構體變量、結構體指針變量作為函數參數4.數組指針變量的初始化結構體變量、結構體指針變量都可以像其他數據類型一樣作為函數的參數,也可以將函數定義為結構體類型或結構體指針類型(返回值為結構體、結構體指針類型)。structstudent{intnum;charname[20];charsex;intage;floatscore;};structstudentstu[3]={{11302,"Wang",'F',20,483},{11303,"Liu",'M',19,503},{11304,"Song",'M',19,471.5}};print(structstudents)
{/*打印學生姓名、年齡、成績。形參:結構體類型*/printf("%s,%d,%5.1f\n",,s.age,s.score);}add10(structstudent*ps){/*年齡≤19,成績加10分。形參:結構體指針類型*/if(ps->age<=19)ps->score=ps->score+10;}例9.10給年齡在19歲以下(含19歲)同學的成績增加10分(二)結構體變量、結構體指針變量作為函數參數main(){structstudent*p;inti;for(i=0;i<3;i++)print(stu[i]);/*循環(huán)打印學生的記錄*/for(i=0,p=stu;i<3;i++,p++)add10(p);/*循環(huán)判斷,加分*/for(i=0,p=stu;i<3;i++,p++)print(*p);/*循環(huán)打印學生的記錄*/}說明(1)因為函數print的形參s屬于結構體類型,所以實參也用結構體類型stu[i]或*p。(2)因為函數add10的形參ps屬于結構體指針類型,所以實參用指針類型&stu[i]或p。(二)結構體變量、結構體指針變量作為函數參數例9.11將例9.10中的函數add10改寫為返回結構體類型值的函數……structstudentadd10(structstudents){if(s.age<=19)s.score=s.score+10;returns;}……main(){structstudent*p;inti;for(i=0;i<3;i++)print(stu[i]);
for(i=0;i<3;i++)stu[i]=add10(stu[i]);for(i=0;i<3;i++)print(stu[i]);*/for(i=0,p=stu;i<3;i++,p++)print(*p);for(i=0,p=stu;i<3;i++,p++)*p=add10(*p);for(i=0,p=stu;i<3;i++,p++)print(*p);}(1)函數add10修改為返回結構體類型的函數,那么形參的傳址就不必要了。(2)主函數調用add10時,將返回值賦值給結構體數組元素。(三)鏈表(結構體指針的應用)1.動態(tài)存儲結構調用形式:(類型說明符*)malloc(size)功能:在內存的動態(tài)存儲區(qū)中分配一塊長度為“size”字節(jié)的連續(xù)區(qū)域。函數的返回值為該區(qū)域的首地址?!邦愋驼f明符”表示把該區(qū)域用于何種數據類型。(類型說明符*)表示把返回值強制轉換為該類型指針。“size”是一個無符號數。例如:pc=(char*)malloc(100);表示分配100字節(jié)的內存空間,并強制轉換為字符數組類型,函數的返回值為指向該字符數組的指針,把該指針賦予指針變量pc。(2)分配內存空間函數calloc()。(1)分配內存空間函數malloc()。calloc也用于分配內存空間。調用形式:(類型說明符*)calloc(n,size)功能:在內存動態(tài)存儲區(qū)中分配n塊長度為“size”字節(jié)的連續(xù)區(qū)域。函數的返回值為該區(qū)域的首地址。(類型說明符*)用于強制類型轉換。calloc函數與malloc函數的區(qū)別僅在于其一次可以分配n塊區(qū)域。例如:ps=(struetstu*)calloc(2,sizeof(structstu));其中,sizeof(structstu)是求stu的結構長度。按stu的長度分配2塊連續(xù)區(qū)域,強制轉換為stu類型,并把其首地址賦予指針變量ps。(三)鏈表(結構體指針的應用)1.動態(tài)存儲結構調用形式:free(void*ptr);功能:釋放ptr指向的一塊內存空間,ptr是一個任意類型的指針變量,它指向被釋放區(qū)域的首地址。被釋放區(qū)域應是由malloc或calloc函數分配的區(qū)域。(3)釋放內存空間函數free()。例9.12分配一塊區(qū)域,輸入一個學生數據main(){structstu{intnum;char*name;charsex;floatscore;}*ps;ps=(structstu*)malloc(sizeof(structstu));ps->num=102;ps->name="Zhangping";ps->sex='M';ps->score=62.5;printf("Number=%d\nName=%s\n",ps->num,ps->name);printf("Sex=%c\nScore=%f\n",ps->sex,ps->score);free(ps);}(三)鏈表(結構體指針的應用)2.鏈表的概念鏈表是一種常見且重要的數據結構,它是動態(tài)進行存儲分配的一種結構,是利用指針鏈在一起的線性組合。在例9.12中采用了動態(tài)分配的方法為一個結構分配內存空間。每次分配一塊空間可用來存放一個學生的數據,這塊空間我們可稱為節(jié)點。有多少個學生就應該申請分配多少塊內存空間,也就是說要建立多少個節(jié)點。用數組的方法必須占用一塊連續(xù)的內存區(qū)域,而使用動態(tài)分配時,每個節(jié)點之間可以是不連續(xù)的(節(jié)點內是連續(xù)的),節(jié)點之間的聯(lián)系可以用指針實現,即在節(jié)點結構中定義一個成員項用來存放下一節(jié)點的首地址,這個用于存放地址的成員,一般稱為指針域??稍诘?個節(jié)點的指針域內存入第2個節(jié)點的首地址,在第2個節(jié)點的指針域內又存放第3個節(jié)點的首地址,如此串連下去直到最后一個節(jié)點。最后一個節(jié)點因無后續(xù)節(jié)點連接,其指針域可賦為0。這樣一種連接方式,在數據結構中稱為“鏈表”。(三)鏈表(結構體指針的應用)
89
76
……
67^鏈表的示意圖。在圖9-5中,沒有存放任何節(jié)點信息的節(jié)點稱為頭節(jié)點,它存放有第1個節(jié)點的首地址,它沒有數據,只是一個指針變量。以下的每個節(jié)點都分為兩個域,一個域是數據域,存放各種實際的數據,如學號num、姓名name、性別sex和成績score等。另一個域為指針域,存放下一節(jié)點的首地址。鏈表中的每個節(jié)點都是同一種結構類型。例如,一個存放學生學號和成績的節(jié)點應為以下結構。structstu{intnum;intscore;structstu*next;}前兩個成員項組成數據域,后一個成員項next構成指針域,它是一個指向stu類型結構的指針變量。(三)鏈表(結構體指針的應用)3.鏈表操作對鏈表的操作主要有以下幾種。(1)建立鏈表。(4)刪除一個節(jié)點。(2)結構的查找與輸出。(3)插入一個節(jié)點。(三)鏈表(結構體指針的應用)
#defineNULL0#defineTYPEstructstu#defineLENsizeof(structstu)structstu{intnum;intage;structstu*next;};TYPE*creat(intn){structstu*head,*pf,*pb;inti;例9.13建立一個3個節(jié)點的鏈表,存放學生數據(為簡單起見,假定學生數據結構中只有學號和年齡兩項。)for(i=0;i<n;i++){pb=(TYPE*)malloc(LEN);printf("inputNumberandAge\n");scanf("%d%d",&pb->num,&pb->age);if(i==0)pf=head=pb;elsepf->next=pb;pb->next=NULL;pf=pb;}return(head);}(三)鏈表(結構體指針的應用)
TYPE*search(TYPE*head,intn){TYPE*p;inti;p=head;while(p->num!=n&&p->next!=NULL)p=p->next;/*不是要找的節(jié)點后移一步*/if(p->num==n)return(p);if(p->num!=n&&p->next==NULL)printf("Node%dhasnotbeenfound!\n",n);}例9.14
寫一個函數,在鏈表中按學號查找該節(jié)點本函數中使用的符號常量TYPE與例9.13的宏定義相同,等于structstu。函數有兩個形參,head是指向鏈表的指針變量,n為要查找的學號。進入while語句,逐個檢查節(jié)點的num成員是否等于n,如果不等于n且指針域不等于NULL(不是最后節(jié)點),則后移一個節(jié)點,繼續(xù)循環(huán)。如找到該節(jié)點,則返回節(jié)點指針。如循環(huán)結束仍未找到該節(jié)點,則輸出“未找到”的提示信息。(三)鏈表(結構體指針的應用)
(1)被刪除節(jié)點是第1個節(jié)點。這種情況只需使head指向第2個節(jié)點即可,即head=pb->next。(2)被刪節(jié)點不是第1個節(jié)點,這種情況使被刪節(jié)點的前一節(jié)點指向被刪節(jié)點的后一節(jié)點即可,即pf->next=pb->next。TYPE*delete(TYPE*head,intnum){TYPE*pf,*pb;if(head==NULL)/*如為空表,輸出提示信息*/{printf("\nemptylist!\n");gotoend;}pb=head;while(pb->num!=num&&pb->next!=NULL)例9.15寫一個函數,刪除鏈表中的指定節(jié)點/*當不是要刪除的節(jié)點,也不是最后一個節(jié)點時,繼續(xù)循環(huán)*/{pf=pb;pb=pb->next;}/*pf指向當前節(jié)點,pb指向下一節(jié)點*/if(pb->num==num){if(pb==head)head=pb->next;/*如找到被刪節(jié)點,且為第一節(jié)點,則使head指向第2個節(jié)點,否則使pf所指節(jié)點的指針指向下一節(jié)點*/elsepf->next=pb->next;free(pb);printf("Thenodeisdeleted\n");}elseprintf("Thenodenotbeenfoud!\n");end:returnhead;}(三)鏈表(結構體指針的應用)例9.15寫一個函數,刪除鏈表中的指定節(jié)點例如,在學生數據鏈表中,要求按照學號順序插入一個節(jié)點。設被插節(jié)點的指針為pi??稍谝韵聨追N情況下插入。(1)原表是空表,只需使head指向被插節(jié)點即可。(4)在表末插入。在這種情況下,使原表末節(jié)點指針域指向被插節(jié)點,被插節(jié)點指針域置為NULL(2)被插節(jié)點值最小,應插入第一節(jié)點之前。使head指向被插節(jié)點,被插節(jié)點的指針域指向原來的第一節(jié)點則可,即pi->next=pb;head=pi;。(3)在其他位置插入。使插入位置前一節(jié)點的指針域指向被插節(jié)點,使被插節(jié)點的指針域指向插入位置的后一節(jié)點,即pi->next=pb;pf->next=pi;。(三)鏈表(結構體指針的應用)
pb->next=pi;pi->next=NULL;TYPE*insert(TYPE*head,TYPE*pi){TYPE*pf,*pb;pb=head;if(h
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 纖維加工過程中的節(jié)能減排考核試卷
- 琥珀蜜蠟拍賣考核試卷
- 礦物學及巖石學考核試卷
- 糕點行業(yè)產品質量評價與監(jiān)督考核試卷
- 臨清市2024-2025學年五年級數學第二學期期末綜合測試模擬試題含答案
- 珠海三中高一下學期期中考試理科生物試題
- 吉林司法警官職業(yè)學院《紀錄片創(chuàng)作與拍攝》2023-2024學年第一學期期末試卷
- 山東蒙陰縣2024-2025學年中考化學試題倒計時模擬卷(2)含解析
- 遼寧省普蘭店市第一中學2025年高三下學期模擬測試(三)語文試題含解析
- 眉山職業(yè)技術學院《兒童舞蹈創(chuàng)編(實驗)》2023-2024學年第二學期期末試卷
- 2024年同等學力人員申請碩士學位英語試卷與參考答案
- 心力衰竭超濾治療
- 消毒管理辦法
- 水處理設備安裝及管道施工方案
- 中國牦牛奶行業(yè)銷售態(tài)勢及消費規(guī)模預測研究報告(2024-2030版)
- 基于web的二手物品交易系統(tǒng)的設計與實現
- 金融墊資三方合作協(xié)議書范文
- 汽車租賃服務方案(投標方案)
- 電梯維保服務投標方案(技術方案)
- 項目2-低頻電療法
- 2024-2030年海外醫(yī)療項目商業(yè)計劃書
評論
0/150
提交評論