結構體是由C語言中的基本數據類型構成的、並用一個標識符來命名的各種變量的組合,其中可以使用不同的數據類型。 結構體類型的定義 Struct結構體名 { 類型標識符1 成員名1; 類型標識符2 成員名2; …… 類型標識符n 成員名n; }; Struct結構體名——結構體類型名
(1)“struct 結構體名”是一個類型名,它和int、float等作用一樣可以用來定義變量。 (2)結構體名是結構體的標識符不是變量名,也不是類型名。 (3)構成結構體的每一個類型變量稱為結構體成員,它像數組的元素一樣,單數組中元素以下標來訪問,而結構體是按結構體變量名來訪問成員的。 (4)結構體中的各成員既可以屬於不同的類型,也可以屬於相同的類型。 (5)成員也可以是一個結構體類型,如: Struct date { Int month; Int day; Int year; }; Struct person { Float num; Char name[20]; Char sex; Int age; Struct date birthday; Char address[10]; };
1.先定義結構體類型,再定義結構體變量 形式: Struct 結構體名 {類型標識符1 成員名1; 類型標識符2 成員名2; …… 類型標識符n 成員名n; }; Struct 結構體名 變量名表; 例如: Struct student {char name[20]; Char sex; Int age; Float score; }; Struct student stu1,stu2; 2.在定義結構體類型的同時定義變量 形式: Struct 結構體名 { 類型標識符1 成員名1; 類型標識符2 成員名2; …… 類型標識符n 成員名n; }變量名表; 例如: Struct student { Char name[20]; Char sex; Int age; Float score; }stu1,stu2; 3.用匿名形式直接定義結構體類型變量 形式: Struct { 類型標識符1 成員名1; 類型標識符2 成員名2; …… 類型標識符n 成員名n; }變量名表; 例如: Struct { Char naem[20]; Char sex; Int age; Float score; }stu1,stu2;
結構體是一種新的數據類型,因此結構體變量也可以像其它類型的變量一樣賦值、運算,不同的是結構體變量以成員作為基本變量。 結構體成員的表示方式為:結構體變量名.成員名 其中的圓點運算符稱為成員運算符,它的運算級別最高。 如果將“結構體變量名.成員名”看成一個整體,則這個整體的數據類型與結構體中該成員的數據類型相同,這樣就可以像前面所講的變量那樣使用,但應遵循以下規則: (1)不能將一個結構體變量作為一個整體進行輸入和輸出,而只能對結構體變量中的各個成員分別進行輸入和輸出。 Struct date { Int month; Int day; Itn year; }day; Scanf(“%d%d%d”,day);(錯誤) Scanf(“%d%d%d”,&day.year,&day.month,&day.day);(正確) Printf(“%d%d%d”,day);(錯誤) Printf(“%d%d%d”,day.year,day.month,day.day);(正確) (2)如果成員本身又屬於一個結構體類型,則要用若干個成員運算符,一級一級地找到最底的一級的成員,只能對最底級的成員進行賦值或存取運算。 Struct date { Int month; Int day; Int year; }; Struct student { Long num; Char naem[20]; Char sex; Int age; Struct date birthday; Char depart[10]; }stu1; 如:stu1.birthday.year=2004; Stu1.birthday.month=12; (3)對結構體變量的成員可以像同類型普通變量一樣進行各種運算。
1.結構體變量的初始化:在定義結構體變量的同時給它賦以初值。 Struct student { Char name[20]; Char sex; Int age; Float score; }stu1,stu2={“wangwu”,’m’,20,88.5}; 2.結構體變量所占內存的字節數 ·struct 類型用內存字節數=? ·是所有成員變量的內存總和嗎? 用運算符sizeof獲得結構體大小 Sizeof(變量或表達式) Sizeof(類型) Typedef struct sample { Char m1; Int m2; Char m3; }SAMPLE; Printf(“%d\n”,sizeof(struct sample)); Printf(“%d\n”,sizeof(SAMPLE)); 並非所有成員變量的內存總和 事實上,所有數據類型在內存中都是從偶數地址開始存放的且結構所占的實際空間一般是按照機器字長對齊的不同編譯器、平台,對齊方式會有變化 結構體變量的成員存儲對齊規則是與機器相關的 具有特定數據類型的數據項大小也是與機器相關的 所以一個結構體在內存中的存儲格式也是與機器相關的 3.結構體變量存儲分配示意圖
1、結構體數組的定義 結構體數組就是具有相同結構體類型的變量集合。 Struct 結構體名 { 類型標識符1 成員名1; 類型標識符2 成員名2; …… 類型標識符n 成員名n; }數組名[整型常量表達式]; 加入要定義一個班級100個同學的學號、姓名、性別、年齡,可以定義成一個結構體數組。如下所示: Struct{ Long num; Char name[20]; Char sex; Int age; }stu[100];
2 結構體數組的初始化 一般形式是在定義的數組後面加上={初始值表列}; Struct student { Long num; Char name[20]; Char sex; Int age; Float score; Char add[5]; }stu[3]={ {101,”WGJ”,’M’,28,88.5,”CS”} {102,”DYH”,’F’,26,88.0,”CS”} {103,”DYC”,’M’,24,78.5,”CS”} };
結構體數組的使用: 結構體數組成員的訪問十一數組元素為結構體變量的,其形式為:結構體數組元素名.成員名 如:stu[2].age 候選人得票的統計。設有三個候選人,每次輸入一個得票的候選人名字,要求最後輸出各人得票結果。 Struct person { Char name[20]; Int count; }leader[3]={“Zhang”,0,”Li”,0,”Wang”,0}; Main() { Int i,j; Char leader_name[20]; For(i=1;i<=10;i++){ Scanf(“%s”,leader_name); For(j=0;j<3;j++) If(strcmp(leader_name,leader[j].name)==0) Leader[j].count++; } Printf(“\n”); For(i=0;i<3;i++) Printf(“%5s:%d\n”,leader[i].name,leader[i].count); }
一個結構體變量的指針就是該變量所占據的內存段的起始地址。可以定義一個指針變量,用來指向一個結構體變量,此時該指針變量的值是結構體變量的起始地址。 1.指向一個結構體變量的指針定義形式: Struct 結構體名 *指針變量名; 例如: Struct student { Long int num; Char name[20]; Char sex; Float score; }; Struct student *pstu,stu; Pstu=&stu;
2.由指向結構體變量的指針變量引用結構體成員的形式為: (*指針變量).成員名 或指針變量名->成員名 ->為指向運算符 如:上面示例用指針變量引用其成員的方式為: (*pstu).num , (*pstu).name (*pstu).sex , (*pstu).score 可以等價表示為: Pstu->num,pstu->name,pstu->sex,pstu->score 比較結構體成員的幾種引用方式 #include “stdio.h” Main(){ Struct student{ Long int num; Char name[20]; Char sex; Float score; }; Struct student stu_1; Struct student *p; P=&stu_1; Stu_1.num=200601; Strcpy(stu_1.name,”liping”); Stu_1.sex=’M’; Stu_1.score=811.5; Printf(“No.:%ld\nname:%ssex:%c\nscore:%f\n”,stu_1.num,sut_1.name,stu_1.score); Printf(“\nNo.:%ld\nname:%s\nsex:%cscore:%f\n”,(*p).num,(*p).name,(*p).sex,(*p).score); Printf(“\nNo.:%ld\nname:%s\nsex:%c\nscore:%f\n”,p->num,p->name,p->sex,p->score); }
指向結構體數組元素的指針 一個指針變量可以指向一個結構體數組元素(將該結構體數組的數組元素地址賦給此指針變量)。例如: Struct { Int a; Float b; }arr[3],*p; P=arr; 此時使p指向arr數組的第一個元素,“p=arr;”等價於“p=&arr[0];”。若執行“p++;”則此時指針變量p此時指向arr[1]。 輸出數組中各元素中各成員的值。 Struct student { Int num; Char name[20]; Char sex; Int age; }; Struct student stu[3]={ {10101,”zhang”,’m’,18}, {10102,”li”,’m’,19}, {10103,”wang”,’f’,20} }; Main(){ Struct student *p; Printf(“No.Name sex age\n”); For(p=stu;pnum,p->name,p->sex,p->age); }
注意:
(1)如果p的初值為stu,即指向第一個元素,則p+1後指向下一個元素。請區別:(++p)->num和(p++)->num
(2)指針p已定義為指向struct student類型的數據,它只能指向該結構體類型數據,而不能指向一元素的某以成員(即p的地址不能是成員的地址)。如下面的賦值是錯誤的
:p=&stu.num編譯時將給出“警告”信息,表示地址的類型不匹配。不要認為反正p是存放地址的,可以將任何地址賦給它。
結構體變量作函數參數 將一個結構體變量的值傳遞給另一個函數,可以采用以下兩種方式: ·用結構體變量的成員作參數。用法和普通變量作實參是一樣的,屬“值傳遞”方式。 ·形參與實參都用結構體變量 直接將實參結構體變量的各個成員之全部傳遞給形參的結構體變量。注意:實參和形參類型應當完全一致。 例 有一個結構體變量stu,內含學生學號、姓名和三門課的成績。要求在main函數中賦值,在另一函數print中將它們打印輸出,程序如下。 Struct stu { Int num; Char name[20]; Int score[3]; }; Main(){ Void print(struct stu p); Struct stu stu;int j; Scanf(“%d”,&stu.num); Scanf(“%s”,stu.name); For(j=0;j<3;j++) Scanf(“%d”,&stu.score[j]); Print(stu); } Void print(struct stu p){ Int j; Printf(“%d\n%s\n”,p.num,p.name); For(j=0;j<3;j++) Printf(“%d\n”,p.score[j]); } 注意:ANSI C允許用整個結構體作為函數的參數傳遞,但是必須保證實參與形參的類型相同。 ·值得指出的是,把一個完整的結構體變量作為參數傳遞,雖然合法,但要將全部成員一個一個傳遞,既費時間又費空間,開銷大,因此一般不采用。
用指向結構體的指針作函數參數 用指向結構體變量(或數組)的起始地址傳給形參。 形參為指針變量,實參為結構體變量的地址或指向結構體變量的指針。 例的print函數形參改用指向結構體指針後程序如下: #include “stdio.h” Struct student { Int num; Char name[20]; Int score[3]; }; Main() {void print(struct student *p); Struct student stu;int j; Scanf(“%d”,&stu.num); Strcpy(stu.name,”liping”); For(j=0;j<3;j++) Scanf(“%d”,&stu.score[j]); Print(&stu); } Void print(struct student *p) { Int j; Printf(“%d\n%s\n”,p->num,p->name); For(j=0;j<3;j++) Printf(“%d\n”,p->score[j]); }
返回結構體類型值的函數 ANSI C還允許函數返回結構體類型的值。設有struct student 類型,結構體變量定義如下: Struct student stud; 若函數input()的功能是輸入一個學生結構體數據,並將其返回給學生記錄stud,即: #include “stdio.h” Struct student {int num; Char name[10]; Int score[3]; }; Struct student input() { Int k; Struct student stud; Scanf(“%d”,&stud.num); Getchar(); Gets(stud.name); For(k=0;k<3;k++) Scanf(“%d”,&stud.score[k]); Return stud; } Main(){ Struct student stud; Int k; Stud=input(); Pritnf(“%d%s”,stud.num,stud.name); For(k=0;k<3;k++) Pritnf(“%d”,stud.score[k]); }
鏈表概述: 鏈表是一種常見的重要的數據結構,它是動態地進行存儲分配的一種結構,根據需要開辟內存單元,使用鏈表可以減少內存空間的浪費。 所謂鏈表是指若干個結構體變量(每個結構體變量稱為一個“結點”)按一定的原則連接起來。每個結構體變量都包含有若干個數據和一個指向下一個結構體變量的指針, 依靠這些指針將所有的結構體變量連接成一個鏈表。 例:建立和輸出一個簡單的鏈表 Struct student { Long num; Char name[20]; Struct student *next; }; Main(){ Struct student a,b,c,*head,*p; A.num=2002001;b.num=2002002;c.num=2002003; Strcpy(a.name,”zhang”); Strcpy(b.name,”sun”); Strcpy(c.name,”li”); Head=&a; A.next=&b; B.Next=&c; C.Next=NULL; P=head; Do { Printf(“%ld,%s”,p->num,p->name); P=p->next; }while(p!=NULL); }
鏈表的基本操作包括建立動態鏈表、鏈表的插入、刪除、輸出和查找等。 1.建立動態鏈表 所謂建立動態鏈表是指一個一個地輸入各結點數據,並建立起各結點前後相鏈的關系。建立動態鏈表有兩種方式:一種是在鏈表尾插入節點,一種是在鏈表頭插入結點。 例:寫一個函數建立一個若干學生數據的單向動態鏈表。(要求按學號從小到大的順序建立) #define NULL 0 Struct student { Long num; Struct student *next; }; Struct student *creat() { Struct student *head,*p,*q; Long x; q=head=(struct student *)malloc(sizeof(struct student)); Head->next=NULL; Printf(“\nplease input datas to the list:”); Scanf(“%ld”,&x); While(x!=0){ P=(struct student *)malloc(sizeof(struct student)); P->num=x; P->next=NULL; q->next=p; q=p; Scanf(“%ld”,&x); } Return (head); }
2.輸出鏈表 Void print(struct student *head) { Struct student *p; P=head->next; Printf(“\nthe list is:”); If(p==NULL) Printf(“the list is NULL!\n”); While(p!=NULL) { Printf(“%6ld”,p->num); P=p->next; } Printf(“\n”); }
3.刪除鏈表中的一個結點 Void del(struct student *head) { Struct student *p,*q; Long num; Printf(“\nplease input the student’ num you want to delete:”); Scanf(“%ld”,&num); P=head->next; While(num!=p->num && p->next!=NULL){ Q=p; P=p->next; } If(num==p->num) {q->next=p->next; Free(p);}else Printf(“\n%ld is not found!\n”,num); }
4.在鏈表中插入一個結點 Void insert(struct student *head) { Struct student *p,*q,*t; P=(struct student *)malloc(sizeof(struct student)); Printf(“\nplease input the student you want to insert:”); Scanf(“%ld”,&p->num); Q=head; While(q-next!=NULL && q->next-numnum) Q=q->next; P->next=q->next; Q->next=p; }
5.對鏈表的綜合操作 Void main(){ Struct student *la; La=creat(); Print(la); Del(la); Print(la); Insert(la); Print(la); }
1、數組的元素個數是固定的,而組成鏈表的結點個數可按需要增減; 2、數組中的元素順序關系由元素在數組中的位置(即下標)確定,鏈表中的結點順序關系由結點所包含的指針來體現。 3、對於不是固定長度的列表,用可能最大長度的數組來描述,會浪費許多內存空間。另外,對於元素的插入、刪除操作非常頻繁的列表處理場合,用數組表示 列表也是不適宜的。若用鏈表實現,會使程序結構清晰,處理的方法也較為簡單。
字符型或其它類型顯然是不妥當的。為此,C語言提供了一種稱為“枚舉”的類型。在“枚舉”類型的定義中列舉出所有可能的取值,被說明為該“枚舉”類型的變量取值不能超過定義的范圍。應該說明的是,枚舉類型是一種基本數據類型,而不是一種構造類型,因為它不能再分解為任何基本類型。
枚舉類型的定義和枚舉變量的說明:
1、枚舉的定義枚舉類型定義的一般形式為:
enum枚舉名{枚舉值表};
在枚舉值表中應羅列出所有可用值。這些值也稱為枚舉元素。例如:
該枚舉名為weekday,枚舉值共有7個,即一周中的七天。凡被說明為weekday類型變量的取值只能是七天中的某一天。
2、枚舉變量的說明
如同結構和聯合一樣,枚舉變量也可用不同的方式說明,即先定義後說明,同時定義說明或直接說明。設有變量a,b,c被說明為上述的weekday,可采用下述任一種方式:
enumweekday{sun,mou,tue,wed,thu,fri,sat};
enumweekdaya,b,c;
或者為:
enumweekday{sun,mou,tue,wed,thu,fri,sat}a,b,c;
或者為:
enum{sun,mou,tue,wed,thu,fri,sat}a,b,c;
枚舉類型在使用中有以下規定: 1、枚舉值是常量,不是變量。不能在程序中用賦值語句再對它賦值。例如對枚舉weekday的元素再作以下賦值: sun=5;mon=2;sun=mon;都是錯誤的。 2、枚舉元素本身由系統定義了一個表示序號的數值,從0開始順序定義為0,1,2…。如在weekday中,sun值為0,mon值為1,…,sat值為6。 main(){ enumweekday {sun,mon,tue,wed,thu,fri,sat}a,b,c; b=mon; c=tue; printf("%d,%d,%d",a,b,c);} 說明:只能把枚舉值賦予枚舉變量,不能把元素的數值直接賦予枚舉變量。如:a=sum;b=mon;是正確的。而: a=0;b=1; 是錯誤的。如一定要把數值賦予枚舉變量,則必須用強制類型轉換。如:a=(enumweekday)2; 其意義是將順序號為2的枚舉元素賦予枚舉變量a,相當於: a=tue; 還應該說明的是枚舉元素不是字符常量也不是字符串常量,使用時不要加單、雙引號。
要點: 1.文件操作,就是通過程序,操作文件 2.兩個方面:讀,寫
函數原型:FILE *fopen(char *filename, char *type);int fclose(FILE *fp); 頭文件:#include是否是標准函數:是 函數功能:函數fopen:打開一個流,即:打開一個文件。該函數有兩個參數,filename是需要打開文件的文件名,type是打開文件的方式。函數fclose:關閉一個流, 即:關閉一個文件,並釋放文件緩沖區。fclose函數與fopen函數是相對的兩個函數。fclose函數的參數是指向文件的指針,應用該函數用以在程序結束之前關閉文件,並釋放 文件緩沖區。這樣可以保證文件的數據不流失。返回值:fopen:FILE類型,如果打開的文件存在,返回指向該文件的指針;如果打開的文件不存在,則在指定的目錄下建立該文件打開, 並返回指向該文件的指針。fclose:整型,有錯返回非0,否則返回0。 例子1: #include int main(void) { FILE *FP=fopen("文件","打開方式");//返回值是file是一個結構體,是一個結構體的宏。 fclose(FP);//打開之後,用完了,就要關閉文件,fclose是跟fopen一起使用的一個函數。 return 0; }
fread:從流中讀取字符串函數 函數原型:int fread(void *buf, int size, int count, FILE *fp); 頭文件:#include是否是標准函數:是 函數功能:從fp指向的文件中讀取長度為size 的count個數據項,並將它輸入到以buf為首地址的緩沖區中。此時,文件指針fp會自動增加實際讀入數據的字節數, 即fp指向最後讀入字符的下一個字符位置。 返回值:返回實際讀入數據項的個數,即count值。若遇到錯誤或文件結束返回0。 例子1: #include int main(void) { FILE *FP=fopen("文件","方式”); if(!FP){ perror("fopen"); exit(-1);//當沒打開的時候就終止 } char buf[64]={};//首先需要一個空間 fread(buf,)//fread就是把文件中的內容讀到一個空間,第一個參數是要存放的一個任意類型的空間,第二個參數是一次讀取的單位信息長度是多少, 第三個參數是要讀取這種長度的信息一共有多少個,第四個參數是讀取那個文件。 fclose(FP); return 0; }
fwrite:向文件寫入數據函數 函數原型:int fwrite(void *buf, int size, int count, FILE *fp); 頭文件:#include是否是標准函數:是 函數功能:將buf所指向的count*size個字節的數據輸出到文件指針fp所指向的文件中去。該函數與fread相對,輸出數據後,文件指針fp自動指向輸出的最後一個字符的下一個位置。 該函數常用於分批將數據塊輸出到文件中。 返回值:返回實際寫入文件數據項個數。 #include #include #include int main(void) { FILE *fp=fopen("./file","w");//w意思是打開時如果沒有這個文件,就會創建這個文件。如果已經存在會將它清空, if (!fp) { perror("fopen"); exit(-1); }//打不開時,報錯,終止程序。 fwrite("hello\n",1,strlen("hello\n"),fp);//第一個參數寫之前,必須有一個內存空間的地址,void*表示人一個空間的首字節的地址,第二個參數是, 寫入的單位信息占多少個字節(長度),第三個參數,一共要寫多少個這樣的單位信息。第四個參數是,寫到哪個文件中去。 //strlen:計算字符串長度函數函數原型: int strlrn (char *str);頭文件:#include 是否是標准函數:是函數功能:求字符串的長度, 也就是求字符串str中有多少個字符返回值:字符串str字符的個數 fclose(fp); return 0; }
#include#include int main(void) //{ char buf[20]; FILE *fp=fopen("./file","r"); if (!fp) { perror("fopen"); exit(-1); } FILE *fp2=fopen("./file2","w'"); if (!fp2) { perror("fopen_2"); } while (1) { size_t ret=fread(buf,1,20,fp); if (ret<=0) { break; } fwrite(buf,1,ret,fp2); } fclose(fp); fclose(fp2); return 0; }