1. 程式人生 > >C程式設計 | 結構體、共用體和連結串列

C程式設計 | 結構體、共用體和連結串列

一、定義和使用結構體變數

1、自己建立結構體型別

C語言允許使用者自己建立由不同型別資料組成的組合型的資料結構,它稱為結構體(structre)

例如在程式中要用的如下圖所示的資料結構,可以在程式中自己建立一個結構體型別

struct Student          //宣告一個結構體型別 struct Studnet 
{	int num;        //學號為整型 
	char name[20];	//姓名為字串 
	char sex;       //性別為字元型 
	int age;        //年齡為整型
	float score;	//成績為實型 
	char addr[30];	//地址為字串 
};	                //注意最後有一個分號 

宣告一個結構體型別的一般形式為:

struct 結構體名

         {成員表列};

結構體型別的名字是由一個關鍵字struct和結構體名組合而成的。結構體名由使用者指定,又稱“結構體標記”(structure tag) 。

花括號內是該結構體所包括的子項,稱為結構體的成員(member)。對各成員都應進行型別宣告,即型別名 成員名;

“成員表列”(member list)也稱為“域表”(field list)每一個成員是結構體中的一個域。成員名命名規則與變數名相同。

說明:

(1) 結構體型別並非只有一種,而是可以設計出許多種結構體型別,各自包含不同的成員。

(2) 成員可以屬於另一個結構體型別

。例如:

struct Date				//宣告一個結構體型別 struct Date 
{	int month;			//月
	int day;			//日
	int year;			//年
}; 


struct Student			//宣告一個結構體型別 struct Student
{   int num;
    char name[20];
    char sex;
    int age;
    struct Date birthday;	//成員birthday屬於struct Date型別
    char addr[30]; 
};

2、定義結構體型別變數

①  先宣告結構體型別,再定義該型別的變數    

②  在宣告型別的同時定義變數

格式:

struct 結構體名

      成員表列

}變數名錶列;

struct Student
{	int num;		
	char name[20];
	char sex;	
	int age;
	float score;
	char addr[30];
}student1, student2;

③  不指定型別名而直接定義結構體型別變數

struct

{    

     成員表列

}變數名錶列;

(1) 結構體型別結構體變數是不同的概念,不要混淆。只能對變數賦值、存取或運算,而不能對一個型別賦值、存取或運算。在編譯時,對型別是不分配空間的,只對變數分配空間

(2) 結構體型別中的成員名可以與程式中的變數名相同,但二者不代表同一物件。

(3) 對結構體變數中的成員(即“域”)可以單獨使用,它的作用與地位相當於普通變數。

3、結構體變數的初始化和引用

例:把一個學生的資訊(包括學號、姓名、性別、住址)放在一個結構體變數中,然後輸出這個學生的資訊。

#include <stdio.h>
int main()
{	struct Student			//宣告結構體型別struct Student
	{	long int num;		//以下4行為結構體的成員
		char name[20];
		char sex;
		char addr[20];
	}a={10101,"Li Lin",'M',"123 Beijing Road"};	//定義結構體變數a並初始化
	printf("NO.:%ld\nname:%s\nsex:%c\naddress:%s\n",a.num,a.name,a.sex,a.addr);
	return 0;
}

執行結果:

說明:

①   在定義結構體變數時可以對它的成員初始化。初始化列表是用花括號括起來的一些常量,這些常量依次賦給結構體變數中的各成員。

注意:對結構體變數初始化,不是對結構體型別初始化

②  可以引用結構體變數中成員的值,引用方式為  結構體變數名.成員名

student1.num=10010;
/*已定義了student1為student型別的結構體變數,則student1.num表示student1變數中的num成員,即student1的num(學號)成員*/

“.”成員運算子,它在所有的運算子中優先順序最高,因此可以把student1.num作為一個整體來看待,相當於一個變數。

printf(″%s\n″,student1);	//企圖用結構體變數名輸出所有成員的值 

如上,不能企圖通過輸出結構體變數名來達到輸出結構體變數所有成員的值只能對結構體變數中的各個成員分別進行輸入和輸出

③  如果成員本身又屬一個結構體型別,則要用若干個成員運算子一級一級地找到最低的一級的成員。只能對最低階的成員進行賦值或存取以及運算。

student1.num=10010;	//結構體變數student1中的成員num
student1.birthday.month=6;	//結構體變數student1中的成員birthday中的成員month

④  對結構體變數的成員可以像普通變數一樣進行各種運算(根據其型別決定可以進行的運算)。

student2.score=student1.score;	//賦值運算
sum=student1.score+student2.score;	//加法運算
student1.age++;			//自加運算

⑤  同類的結構體變數可以互相賦值。

student1=student2;		//假設student1和student2已定義為同類型的結構體變數

⑥  可以引用結構體變數成員的地址也可以引用結構體變數的地址(結構體變數的地址主要用作函式引數,傳遞結構體變數的地址)。

scanf(″%d″,&student1.num);	//輸入student1.num的值
printf(″%o″,&student1);	//輸出結構體變數student1的起始地址

不能用以下語句整體讀入結構體變數

scanf(″%d,%s,%c,%d,%f,%s\n″,&student1);   //錯誤語句,不能整體讀入

例:輸入兩個學生的學號、姓名和成績,輸出成績較高的學生的學號、姓名和成績。

#include <stdio.h>
int main()
{	struct Student			//宣告結構體型別struct Student 
	{	int num;
		char name[20];
		float score;
	}student1,student2;	//定義兩個結構體變數student1,student2 
	scanf("%d%s%f",&student1.num,student1.name,&student1.score);	//輸入學生1的資料
	scanf("%d%s%f",&student2.num,student2.name,&student2.score);	//輸入學生1的資料
	printf("The higher score is:\n");
	if(student1.score>student2.score)
		printf("%d  %s  %6.2f\n",student1.num,student1.name,student1.score);
	else if(student1.score<student2.score)
		printf("%d  %s  %6.2f\n",student2.num,student2.name,student2.score);
	else
	{	printf("%d  %s  %6.2f\n",student1.num,student1.name,student1.score);
		printf("%d  %s  %6.2f\n",student2.num,student2.name,student2.score);
	}
	return 0;
}

執行結果:

二、使用結構體陣列

1、定義結構體陣列

  • 定義結構體陣列一般形式是

①  在宣告型別的同時定義結構體陣列

struct 結構體名

{   成員表列

} 陣列名[陣列長度];

struct Person
{	char name[20];
	int count;
} leader[3];

②  先宣告一個結構體型別,然後再用此型別定義結構體陣列 

結構體型別陣列名[陣列長度];

struct Person
{	char name[20];
	int count;
}
struct Person leader[3];	//leader是結構體陣列名
  • 對結構體陣列初始化的形式是在定義陣列的後面加上={初值表列}; 
struct Person leader[3]= {"Li",0,"Zhang",0,"Sun",0};

例:有3個候選人,每個選民只能投票選一人,共10個選民,要求編一個統計選票的程式,先後輸入被選人的名字,最後輸出各人得票結果。

#include <string.h>
#include <stdio.h>
struct person                               // 宣告結構體型別struct person
  {char name[20];                           // 候選人姓名
   int count;                               // 候選人得票數 
  }leader[3]={"li",0,"zhang",0,"sun",0};    // 定義結構體陣列並初始化
int 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("\nResult:\n");
   for(i=0;i<3;i++)
     printf("%5s:%d\n",leader[i].name,leader[i].count);
   return 0;
  }

執行結果:

2、結構體陣列的應用舉例

有n個學生的資訊(包括學號、姓名、成績),要求按照成績的高低順序輸出各學生的資訊。

#include <stdio.h>
struct Student					//宣告結構體型別struct Student
{	int num;
	char name[20];
	float score;
}; 
int main()
{	struct Student stu[5]={{10101,"Zhang",78},{10103,"Wang",98.5},{10106,"Li",86},
	{10108,"Ling",73.5},{10110,"Sun",100}};			//定義結構體陣列並初始化 
	struct Student temp;		//定義結構體變數temp,用作交換時的臨時變數
	const int n=5;				//定義常變數n
	int i,j,k;
	printf("The order is:\n");
	for(i=0;i<n-1;i++)
	{	k=i;
		for(j=i+1;j<n;j++)
			if(stu[j].score>stu[k].score)			//進行成績的比較
				k=j;
		temp=stu[k]; stu[k]=stu[i]; stu[i]=temp;	//stu[k]和stu[i]元素互換
	}
	for(i=0;i<n;i++)
		printf("%6d %8s %6.2f\n",stu[i].num,stu[i].name,stu[i].score);
	printf("\n");
	return 0;
}

執行結果:

三、結構體指標

所謂結構體指標就是指向結構體變數的指標一個結構體變數的起始地址就是這個結構體變數的指標。如果把一個結構體變數的起始地址存放在一個指標變數中,那麼,這個指標變數就指向該結構體變數。

1、指向結構體變數的指標

例:通過指向結構體變數的指標變數輸出結構體變數中成員的資訊。

#include <stdio.h>
#include <string.h>
int main()
{	struct Student			//宣告結構體型別struct Student
	{	long num;
		char name[20];
		char sex;
		float score;
	};
	struct Student stu_1;	//定義struct Student型別的變數stu_1 
	struct Student *p;		//定義指向struct Student 型別資料的指標變數p 
	p=&stu_1;			//p指向stu_1 
	stu_1.num=10101;		//對結構體變數的成員賦值 
	strcpy(stu_1.name,"Li Lin");	//用字串複製函式給stu_1.name賦值
	stu_1.sex='M';
	stu_1.score=89.5;
	printf("No.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n",stu_1.num,stu_1.name,stu_1.sex,stu_1.score);	//輸出結果 
	printf("\nNo.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n",(*p).num,(*p).name,(*p).sex, (*p).score);	
	return 0;
}

執行結果:

程式分析:(*p).num 也可表示為 p->num

如果p指向一個結構體變數stu,以下3種用法等價:

① stu.成員名      eg:stu.num

(*p).成員名     eg:(*p).num

③  p->成員名      eg:p->num

2、指向結構體陣列的指標

例:

#include <stdio.h>
struct Student				//宣告結構體型別struct Student
{	int num;
	char name[20];
	char sex;
	int age;
};
struct Student stu[3]={{10101,"Li Lin",'M',18},{10102,"Zhang Fang",'M',19},{10104,"Wang Min",'F',20}};
//定義結構體陣列並初始化 
int main()
{	struct Student *p;		//定義指向struct Student結構體變數的指標變數 
	printf(" No.   Name                sex  age\n");
	for (p=stu;p<stu+3;p++)
	printf("%5d %-20s %2c %4d\n",p->num, p->name, p->sex, p->age);	//輸出結果
	return 0;
}

執行結果:

3、用結構體變數和結構體變數的指標作函式引數

一個結構體變數的值傳遞給另一個函式,有3個方法:  

(1) 用結構體變數的成員作引數

例如,用stu[1].num或stu[2].name作函式實參,將實參值傳給形參。用法和用普通變數作實參是一樣的,屬於“值傳遞”方式。應當注意實參與形參的型別保持一致

(2) 用結構體變數作實參 

用結構體變數作實參時,採取的也是“值傳遞”的方式,將結構體變數所佔的記憶體單元的內容全部按順序傳遞給形參,形參也必須是同類型的結構體變數。在函式呼叫期間形參也要佔用記憶體單元。這種傳遞方式在空間和時間上開銷較大,如果結構體的規模很大時,開銷是很可觀的。此外,由於採用值傳遞方式,如果在執行被呼叫函式期間改變了形參(也是結構體變數)的值,該值不能返回主調函式,這往往造成使用上的不便。因此一般較少用這種方法。

(3) 用指向結構體變數(或陣列元素)的指標作實參,將結構體變數(或陣列元素)的地址傳給形參。

例:有n個結構體變數,內含學生學號、姓名和3門課程的成績。要求輸出平均成績最高的學生的資訊(包括學號、姓名、3門課程成績和平均成績)。

#include <stdio.h>
#define N 3			//學生數為3
struct Student			//建立結構體型別struct Student
{	int num;			//學號
	char name[20];	//姓名
	float score[3];		//3門課成績
	float aver;		//平均成績
};
int main()
{	void input(struct Student stu[]);	//函式宣告
	struct Student max(struct Student stu[]);	//函式宣告
	void print(struct Student stu);	//函式宣告
	struct Student stu[N],*p=stu;	//定義結構體陣列和指標
	input(p);						//呼叫input函式
	print(max(p));	//呼叫print函式,以max函式的返回值作為實參
	return 0;
}
void input(struct Student stu[])		//定義input函式
{	int i;
	printf("請輸入各學生的資訊: 學號、姓名、三門課成績:\n");
	for(i=0;i<N;i++)
	{	scanf("%d %s %f %f %f",&stu[i].num,stu[i].name,
		&stu[i].score[0],&stu[i].score[1],&stu[i].score[2]);	 //輸入資料
		stu[i].aver=(stu[i].score[0]+stu[i].score[1]+stu[i].score[2])/3.0;//求平均成績
	}
}
struct Student max(struct Student stu[])	//定義max函式
{	int i,m=0;			//用m存放成績最高的學生在陣列中的序號
	for(i=0;i<N;i++)
	if(stu[i].aver>stu[m].aver) m=i;    //找出平均成績最高的學生在陣列中的序號
	return stu[m];		//返回包含該生資訊的結構體元素
}

void print(struct Student stud) 		//定義print函式
{	printf("\n成績最高的學生是:\n");
	printf("學號:%d\n姓名:%s\n三門課成績:%5.1f,%5.1f,%5.1f\n平均成績: %6.2f\n",stud.num,stud.name,stud.score[0],stud.score[1],stud.score[2],stud.aver);
}

執行結果

四、用指標處理連結串列

1、什麼是連結串列

連結串列是一種常見的重要的資料結構。它是動態地進行儲存分配的一種結構

連結串列有一個“頭指標”變數,圖中以head表示,它存放一個地址,該地址指向一個元素。

連結串列中每一個元素稱為“結點”,每個結點都應包括兩個部分:

 (1) 使用者需要用的實際資料;  

(2) 下一個結點的地址。 head指向第1個元素,第1個元素又指向第2個元素……直到最後一個元素,該元素不再指向其他元素,它稱為“表尾”,它的地址部分放一個“NULL”(表示“空地址”),連結串列到此結束。

可以用結構體變數建立連結串列。一個結構體變數包含若干成員,這些成員可以是數值型別、字元型別、陣列型別,也可以是指標型別。用指標型別成員來存放下一個結點的地址

struct Student
{	int num;
	float score;
	struct Student *next; 	//next是指標變數,指向結構體變數
}; 

成員num和score用來存放結點中的有用資料(使用者需要用到的資料)。

next是指標型別的成員,它指向struct Student型別資料(就是next所在的結構體型別)

注意:上面只是定義了一個struct Student型別,並未實際分配儲存空間,只有定義了變數才分配儲存單元

2、建立簡單的靜態連結串列

例:建立一個簡單鏈表,它由3個學生資料的結點組成,要求輸出各結點中的資料。

#include <stdio.h>
struct Student						//宣告結構體型別struct Student
{	int num;
	float score;
	struct Student*next;
};
int main()
{	struct Student a,b,c,*head,*p;	//定義3個結構體變數a,b,c作為連結串列的結點
	a.num=10101; a.score=89.5;		//對結點a的num和score成員賦值
	b.num=10103; b.score=90;		//對結點b的num和score成員賦值
	c.num=10107; c.score=85;		//對結點c的num和score成員賦值
	head=&a;					//將結點a的起始地址賦給頭指標head
	a.next=&b;					//將結點b的起始地址賦給a結點的next成員
	b.next=&c;					//將結點c的起始地址賦給a結點的next成員
	c.next=NULL;					//c結點的next成員不存放其他結點地址
	p=head;						//使p指向a結點
	do
	{	printf("%ld %5.1f\n",p->num,p->score);	//輸出p指向的結點的資料
		p=p->next;				//使p指向下一結點
	}while(p!=NULL);				//輸出完c結點後p的值為NULL,迴圈終止
	return 0;
}

執行結果:

 

3、建立簡單的動態連結串列

例:寫一函式建立一個有3名學生資料的單向動態連結串列。

所謂建立動態連結串列是指在程式執行過程中從無到有地建立起一個連結串列,即一個一個地開闢結點和輸入各結點資料,並建立起前後相鏈的關係。

#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(struct Student)
struct Student
{	long num;
	float score;
	struct Student*next;
};
int n; 	//n為全域性變數,本檔案模組中各函式均可使用它
struct Student *creat(void)   //定義函式。此函式返回一個指向連結串列頭的指標
{	struct Student *head;
	struct Student *p1,*p2;
	n=0;
	p1=p2=(struct Student*) malloc(LEN);  //開闢一個新單元
	scanf("%ld,%f",&p1->num,&p1->score);  //輸入第1個學生的學號和成績
	head=NULL;
	while(p1->num!=0)
	{	n=n+1;
		if(n==1) head=p1;
		else p2->next=p1;
		p2=p1;
		p1=(struct Student*)malloc(LEN);   //開闢動態儲存區,把起始地址賦給p1
		scanf("%ld,%f",&p1->num,&p1->score);   //輸入其他學生的學號和成績
	}
	p2->next=NULL;
	return(head);
}
int main()
{	struct Student *pt;
	pt=creat(); 	//函式返回連結串列第一個結點的地址 
	printf("\nnum:%ld\nscore:%5.1f\n",pt->num,pt->score);  //輸出第1個結點的成員值
	return 0;
};

執行結果:

程式分析:

如果p1->num不等於0,則輸入的是第一個結點資料(n=1),令head=p1,即把p1的值賦給head,也就是使head指向新開闢的結點。p1所指向的新開闢的結點就成為連結串列中第一個結點。然後再開闢另一個結點並使p1指向它,接著輸入該節點的資料。

如果輸入的p1->num≠0,則應鏈入第2個結點(n=2),由於n≠1,則將p1的值賦給p2->next,此時p2指向第一個結點,因此執行“p2->next”後,就將新結點的地址賦給第一個結點的next成員,使第1個結點的next成員指向第2個結點。接著使p2=p1,也就是使p2指向剛才建立的結點。到此,第1個與第2個結點就形成了連結串列···

4、輸出連結串列

例:編寫一個輸出連結串列的函式print。

#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(struct Student)
struct Student					//宣告結構體型別struct Student
{	long num;
	float score;
	struct Student *next;
};
int n;						//全域性變數n
void print(struct Student*head)	//定義print函式 
{	struct Student*p;			//在函式中定義struct Student型別的變數p
	printf("\nNow,These %d records are:\n",n);
	p=head;					//使p指向第1個結點
	if(head!=NULL)			//若不是空表
		do
		{	printf("%ld %5.1f\n",p->num,p->score);	//輸出一個結點中的學號與成績
			p=p->next;		//p指向下一個結點
		}while(p!=NULL);		//當p不是"空地址"
}

注意:以上只是一個函式,可以單獨編譯,不能單獨執行

例:綜合程式——使用建立和輸出動態連結串列

#include <stdio.h>
#include <malloc.h>
#define LEN sizeof(struct Student)
struct Student
{	long num;
	float score;
	struct Student *next;
};
int n; 
struct Student *creat()	//建立連結串列的函式 
{	struct Student *head;
	struct Student *p1,*p2;
	n=0;
	p1=p2=(struct Student *)malloc(LEN);
	scanf("%ld,%f",&p1->num,&p1->score);
	head=NULL;
	while(p1->num!=0)
	{	n=n+1;
		if(n==1) head=p1;
		else p2->next=p1;
		p2=p1;
		p1=(struct Student *)malloc(LEN);
		scanf("%ld,%f",&p1->num,&p1->score);
	}
	p2->next=NULL;
	return(head);
}

void print(struct Student *head)	//輸出連結串列的函式 
{	struct Student *p;
	printf("\nNow,These %d records are:\n",n);
	p=head;
	if(head!=NULL)
		do
		{	printf("%ld %5.1f\n",p->num,p->score);
			p=p->next;
		}while(p!=NULL);
}

int main()
{	struct Student *head;
	head=creat();	//呼叫creat函式,返回第1個結點的起始地址
	print(head);	//呼叫print函式 
	return 0;
}

執行結果:

五、共用體型別

1、什麼是共用體型別

使幾個不同型別的變數共享同一段記憶體的結構,稱為 “共用體”型別的結構。

定義共用體型別變數的一般格式為

union   共用體名

{    成員表列

}變量表列;

例如: 

union Data
{	int i;          //表示不同型別的變數i,ch,f可以存放到同一段儲存單元中
	char ch;
	float f; 
}a,b,c;				//在宣告型別同時定義變數

也可以將型別宣告與變數定義分開

union Data			//宣告共用體型別
{	int i;
	char ch;
	float f; 
};
union Data a,b,c;	//用共用體型別定義變數

也可以直接定義共用體變數

union		//沒有定義共用體型別名
{	int i;
	char ch;
	float f; 
}a,b,c;

“共用體”與“結構體”的定義形式相似。但它們的含義是不同的

結構體變數所佔記憶體長度各成員佔的記憶體長度之和。每個成員分別佔有其自己的記憶體單元。而共用體變數所佔的記憶體長度等於最長的成員的長度。幾個成員共用一個記憶體區。

2、引用共用體變數的方式

只有先定義了共用體變數才能引用它,但應注意,不能引用共用體變數,而只能引用共用體變數中的成員

a.i 		//引用共用體變數中的整型變數i
a.ch		//引用共用體變數中的字元變數ch
a.f		//引用共用體變數中的實型變數f 
printf(″%d″,a);     //錯誤,不能引用共用體變數
printf(″%d″,a.i);   //正確,可以引用共用體變數中的成員

3、共用體型別資料的特點

(1) 同一個記憶體段可以用來存放幾種不同型別的成員,但在每一瞬時只能存放其中一個成員,而不是同時存放幾個。

union Date
{	int i;
	char ch;
	float f;
}a;
a.i=97;
printf(″%d″,a.i); 	//輸出整數97
printf(″%c″,a.ch);	//輸出字元′a′
printf(″%f″,a.f);	//輸出實數0.000000

(2) 可以對共用體變數初始化,但初始化表中只能有一個常量

union Date
{	int i;
	char ch;
	float f;
}a{1,'a',1.5};    //錯誤,不能初始化3個成員,它們共同佔用同一段儲存單元
    union Data a={16};     //正確,對第1個成員初始化
    union Data a={.ch='j'};   //C 99允許對指定的一個成員初始化

(3) 共用體變數中起作用的成員是最後一次被賦值的成員,在對共用體變數中的一個成員賦值後,原有變數儲存單元中的值就被取代。

(4) 共用體變數的地址和它的各成員的地址都是同一地址

(5) 不能對共用體變數名賦值,也不能企圖引用變數名來得到一個值。C 99允許同類型的共用體變數互相賦值

① a=1; //不能對共用體變數名賦值,賦給誰?
② m=a; //企圖引用共用體變數名以得到一個值賦給整型變數m

③ b=a;	//a和b是同類型的共用體變數,合法

(6) C 99允許用共用體變數作為函式引數

(7) 共用體型別可以出現在結構體型別定義中,也可以定義共用體陣列。反之,結構體也可以出現在共用體型別定義中,陣列也可以作為共用體的成員。

例:有若干個人員的資料,其中有學生和教師。學生的資料中包括: 姓名、號碼、性別、職業、班級。教師的資料包括: 姓名、號碼、性別、職業、職務。要求用同一個表格來處理。

#include <stdio.h>
struct						//宣告無名結構體型別
{	int num;					//成員num(編號)
	char name[10];			//成員name(姓名)
	char sex;					//成員sex(性別)
	char job;					//成員job(職業)
	union					//宣告無名共用體型別
	{	int clas;				//成員clas(班級)
		char position[10];		//成員position(職務) 
	}category;				//成員category是共用體變數
}person[2];					//定義結構體陣列person,有兩個元素
int main()
{	int i;
	for(i=0;i<2;i++)
	{	printf("please enter the data of person:\n");
		scanf("%d %s %c %c",&person[i].num,person[i].name,&person[i].sex,&person[i].job);
		if(person[i].job=='s')
			scanf("%d",&person[i].category.clas);  //如是學生,輸入班級
		else if(person[i].job=='t')
			scanf("%s",person[i].category.position);  //如是教師,輸入職務
		else
			printf("Input error!");		//如job不是's'和't',顯示“輸入錯誤”
	}
	printf("\n");
	  printf("No.  name      sex job class/position\n");
	for(i=0;i<2;i++)
	{	if (person[i].job=='s')	//若是學生
	printf("%-6d%-10s%-4c%-4c%-10d\n",person[i].num,person[i].name,person[i].sex,person[i].job,person[i].category.clas);
		else	//若是教師
	printf("%-6d%-10s%-4c%-4c%-10s\n",person[i].num, person[i].name,person[i].sex,person[i].job,person[i].category.position);
	}
	return 0;
}

執行結果:

六、使用列舉型別

如果一個變數只有幾種可能的值,則可以定義為列舉(enumeration)型別,所謂“列舉”就是指把可能的值一一列舉出來,變數的值只限於列舉出來的值的範圍內。

宣告列舉型別用enum開頭,例如:

enum Weekday{sun,mon,tue,wed,thu,fri,sat};

花括號中的sun,mon,…,sat 稱為列舉元素列舉常量

也可以不宣告有名字的列舉型別,而直接定義列舉變數,例如:

enum {sun,mon,tue,wed,thu,fri,sat}workday,weekend;

宣告列舉型別的一般形式為

enum [列舉名] {列舉元素列表}

說明:

  • C編譯對列舉型別的列舉元素按常量處理,故稱列舉常量。不要因為它們是識別符號(有名字)而把它們看作變數,不能對它們賦值。
  • 每一個列舉元素都代表一個整數,C語言編譯按定義時的順序預設它們的值為0,1,2,3,4,5…。也可以在定義列舉型別時顯式地指定列舉元素的數值。
  • 列舉元素可以用來作判斷比較。列舉元素的比較規則是按其在初始化時指定的整數來進行比較的。

例:口袋中有紅、黃、藍、白、黑5種顏色的球若干個。每次從口袋中先後取出3個球,問得到3種不同顏色的球的可能取法,輸出每種排列的情況。

#include <stdio.h>
int main()
{	enum Color {red,yellow,blue,white,black};					//宣告列舉型別enum Color
	enum Color i,j,k,pri;								//定義列舉變數i,j,k,pri
	int n,loop;
	n=0;
	for(i=red;i<=black;i++)								//外迴圈使i的值從red變到black
		for(j=red;j<=black;j++)							//中迴圈使j的值從red變到black
			if(i!=j)	//如果二球不同色
			{	for (k=red;k<=black;k++)				//內迴圈使k的值從red變到black
					if ((k!=i) && (k!=j))				//如果3球不同色
					{	n=n+1;					//符合條件的次數加1
						printf("%-4d",n);				//輸出當前是第幾個符合條件的組合
						for(loop=1;loop<=3;loop++)		//先後對3個球分別處理
						{	switch (loop)			//loop的值從1變到3
							{	case 1: pri=i;break;	//loop的值為1時,把第1球i的顏色賦給pri
								case 2: pri=j;break;	//loop的值為2時,把第2球j的顏色賦給pri 
								case 3: pri=k;break;	//loop的值為3時,把第3球k的顏色賦給pri
								default:break;
							}
							switch (pri)//根據球的顏色輸出相應的文字
							{	case red:printf("%-10s","red");break;		//pri的值等於列舉常量red時輸出"red"
								case yellow: printf("%-10s","yellow");break;	//pri的值等於列舉常量yellow時輸出"yellow"
								case blue: printf("%-10s","blue");break;	//pri的值等於列舉常量blue時輸出"blue" 
								case white: printf("%-10s","white");break;	//pri的值等於列舉常量white時輸出"white"
								case black: printf("%-10s","black"); break;	//pri的值等於列舉常量black時輸出"black"
								default:break;
							}
						}
						printf("\n");
					}
			}
	printf("\ntotal:%5d\n",n);
	return 0;
}

執行結果:

七、用typedef宣告新型別名

除了可以直接使用C語言提供的標準型別名(如int,char,float,double和long等)和程式編寫者自己宣告的結構體、共用體、列舉型別外,還可以用typedef指定新的型別名來代替已有的型別名,有以下兩種情況:

1、簡單地用一個新的型別名代替原有的型別名

typedef int Integer;	//指定用Integer為型別名,作用與int相同
typedef float Real;	//指定用Real為型別名,作用與float相同

2、命名一個簡單的型別名代替複雜的型別表示方法

① 命名一個新的型別名代表結構體型別    

② 命名一個新的型別名代表陣列型別

③ 命名一個新的型別名代表指標型別    

④命名一個新的型別名代表指向函式的指標型別

typedef struct
{	int month;
	int day;
	int year; 
}Date;				//聲明瞭一個新型別名Date,代表結構體型別
Date birthday;			//定義結構體型別變數birthday,不要寫成struct Date birthday; 
Date*p;				//定義結構體指標變數p,指向此結構體型別資料

typedef int Num[100];	//宣告Num為整型陣列型別名
Num a;				//定義a為整型陣列名,它有100個元素

typedef char*String;	//宣告String為字元指標型別
String p,s[10];			//定義p為字元指標變數,s為字元指標陣列

typedef int (*Pointer)();	//宣告Pointer為指向函式的指標型別,該函式返回整型值
Pointer p1,p2;			//p1,p2為Pointer型別的指標變數

宣告一個新的型別名的方法是:

① 先按定義變數的方法寫出定義體(如: int i;)

② 將變數名換成新型別名(例如: 將i換成Count)

③ 在最前面加typedef(例如: typedef int Count)    

④ 然後可以用新型別名去定義變數

簡單地說,就是按定義變數的方式把變數名換上新型別名,並在最前面加typedef,就聲明瞭新型別名代表原來的型別。 以定義上述的陣列型別為例來說明:

① 先按定義陣列變數形式書寫: int a[100]        

② 將變數名a換成自己命名的型別名: int Num[100]

③ 在前面加上typedef,得到typedef int Num[100]    

④ 用來定義變數: Num a; 相當於定義了: int a[100];

同樣,對字元指標型別,也是:

① char *p;        //定義變數p的方式    

② char *String;    //用新型別名String取代變數名p

③ typedef char *String;    //加typedef        

④ String p;    //用新型別名String定義變數,相當char *p;

習慣上,常把用typedef宣告的型別名的第1個字母用大寫表示,以便與系統提供的標準型別識別符號相區別

說明:

(1) typedef的方法實際上是為特定的型別指定了一個同義字(synonyms)。

(2) 用typedef只是對已經存在的型別指定一個新的型別名,而沒有創造新的型別

(3) 用tyoedef宣告陣列型別、指標型別,結構體型別、共用體型別、列舉型別等,使得程式設計更加方便。

(4) typedef與#define表面實質不同的。#define是在預編譯時處理的,它只能作簡單的字串替換,而typedef是在編譯階段處理的,且並非簡單的字串替換。

(5) 當不同原始檔中用到同一型別資料(尤其是像陣列、指標、結構體、共用體等型別資料)時,常用typedef宣告一些資料型別。可以把所有的typedef名稱宣告單獨放在一個頭檔案中,然後在需要用到它們的檔案中用#include指令把它們包含到檔案中。這樣程式設計者就不需要在各檔案中自己定義typedef名稱了。

(6) 使用typedef名稱有利於程式的通用與移植。有時程式會依賴於硬體特性,用typedef型別就便於移植。