第七章結構體、聯合和枚舉
7.1 結構體的說明和定義
7.1.1 什麼是結構體
前麵的章節已講過數組。數組是具有相同數據類型的數據所組成的集合體,它的引入為程序設計者帶來了很大的方便。但是,在程序設計中,還經常遇到一些關係密切、數據類型不同的數據。對於這些數據,為了處理方便,經常把它們組織在一起,並為其取一個名字。這類數據在Pascal和COBOL語言中叫記錄,而在C語言中叫結構體。
結構體通常是由不同類型的數據所組成的集合體。構成結構體的數據稱之為結構體成員(或稱結構體元素),每一個成員具有不同的名字和數據類型(特殊情況下,也可具有相同的數據類型)。為了處理方便,每個結構體都有一個名字,而所有的成員都組織在該名字之下。結構體的典型例子是學生的記錄,它包括學號、姓名、性別、年齡、地址和電話號碼等,這些數據的類型都可以不同。因此,結構體的每一個成員都是通過其名字來引用的,而不像數組是通過下標來引用。
結構體的引入為處理複雜的數據結構體提供了有力的手段,也為函數間傳遞一組不同數據類型的數據提供了方便。特別是對於數據結構體比較複雜的大型程序提供了方便。
7.1.2 結構體的說明及結構體變量的定義
1.結構體的說明
由於結構體是由不同數據類型的數據所組成的集合體,它包含若幹成員。因此,在使用結構體進行數據處理時,首先應對結構體的組成進行描述。這種描述稱之為說明。結構體的說明實質上是宣布該結構體是由哪些成員所組成,以及成員的數據類型。
結構體說明的一般格式如下:
struct 結構體名
{
結構體成員表列;
}
其中struct是保留字,“struct結構體名”稱結構體類型標識符,或稱結構體類型名。大括號中的結構體成員表列稱結構體體。成員表包含若幹成員,每一個成員都具有如下的形式:
數據類型標識符 結構體成員名;
例如:
struct date
{
int year;
int month;
int day;
int yearday;
};
其中,date是結構體名。而year、month、day、yearday均是該結構體的成員。
有關結構體說明請注意如下幾點:
?結構體說明描述了結構體的組織形式,但在編譯時並不為它分配存儲空間。隻是規定了一種特定的數據結構體類型及它所占用的存儲空間的存儲模型。
?結構體的成員可以是簡單變量、數組、指針、結構體或聯合等。
?結構體說明可以在函數內部,也可以在函數外部。在內部說明的結構體,隻在函數內部可見,在外部說明的結構體,從說明點到源文件尾之間的所有函數都可見。
?結構體可以嵌套使用,即一個結構體也可以成為另一個結構體的成員。
?結構體成員的名字可以同程序中的其他變量同名,二者不會相混。
2.結構體變量的定義
結構體變量同其他變量一樣也必須先定義或說明,然後才能引用。在說明了一個結構體之後,就可定義該結構體的對象了。一個結構體的對象或實例稱之為該結構體類型的結構體變量。可以采用如下三種方式定義結構體變量。
(1)在結構體說明的同時定義結構體變量
其一般形式如下:
[存儲類型]struct結構體名
{
結構體成員表列;
}結構體變量名表列;
其中,存儲類型表示結構體變量的存儲類型(下同)。
例如:
struct example
{
char * name;
int age;
double salary;
char * address;
}guo,zhang,xiao,yang;
該例的結構體名為example,用它定義了guo、zhang、xiao和yang四個結構體變量,這四個變量具有相同的結構體組成,即具有相同的類型。這種定義方法的特點是:定義一次結構體變量之後,在該定義之後任何位置還可用該結構體類型來定義其他結構體變量。
(2)直接定義結構體變量
其一般形式如下:
[存儲類型]struct
{
結構體成員表列;
}結構體變量名表列;
例如:
struct
{
char * name;
int age;
double salary;
char * address;
}guo,zhang,xiao,yang;
這裏定義的四個結構體變量與(1)中定義的完全相同。這種定義方式的特點是:不能在別處用來另行定義別的結構體變量,要想定義就得將“struct{…}”這部分重新寫。
(3)把定義和說明分開
其定義的一般形式如下:
[存儲類型]struct結構體名 結構體變量表;
例如:
struct example
{
char * name;
int age;
double salary;
char * address;
};
利用結構體類型“struct example”來定義結構體變量guo、zhang、xiao和yang的形式為:
struct example guo,zhang,xiao,yang;
這種定義方式的特點是:可把其結構說明部分作為文件存放起來,這樣就可借助於“# include”語句把它複製到任何源文件中,用以定義同類型的其他結構體變量。
有關結構體變量的定義還需如下幾點:
?結構體變量分配內存,而結構體說明不分配內存。
?結構體變量一般不用register型。
?結構體變量的定義一定要在結構體說明之後或與結構體說明同時進行,對尚未說明的結構體類型,不能用它來定義結構體變量。
?結構體變量占用實際內存的大小可用sizeof()運算來求出。sizeof()的一般格式如下:
sizeof(運算符)
其中,運算量可以是簡單變量、數組、結構體或數據類型名。
例如:
sizeof(struct example)=15
sizeof(int)=2
?結構體變量中的成員可以單獨使用,其地位與一般變量相同。
7.2 結構體成員的引用與結構體變量的初始化
7.2.1 結構體成員的引用
在對結構體進行引用時,一般隻能對其成員進行直接操作,而不準許結構體變量整體直接進行操作。
引用有兩種方式:
(1)用指針方式
即定義一個指針,使它指向該結構體變量,這時就可用指針和成員名來引用結構體成員了。
(2)用結構體成員運算符方式。
用結構體成員運算符引用結構體成員的一般形式如下:
結構體變量名.成員名
其中“.”是結構體成員運算符,其結合性是自左至右。下麵是用結構體成員運算符引用結構體成員的例子:
struct example
{
long int idnumber;
char * name;
char address[100];
}guo;
各成員的引用形式如下:
guo.idnumber
guo.name
guo.address或guo.address[i]
對於* guo.name形式,由於運算符“.”優先於“*”所以,* guo.name等價於*(guo.name)。其含義是訪問guo.name的目標變量。
【例7-1】將某年某月某日轉換成該年的第幾天。
這裏我們把與時間有關的變量定義為結構體變量,其程序如下:
main()
{
struct date / * 定義結構體 * /
{
int year; / * 定義結構體成員及其數據類型 * /
int month;
int day;
int yearday;
char * monthname;
}date1; / * 定義結構體變量 * /
int leap;
int i;
static int month[2][13] ={
{0,31,28,31,30,31,30,31,31,30,31,30,31}, / *定義靜態數組 * /
{0,31,29,31,30,31,30,31,31,30,31,30,31}};
printf(" \ n Please enter year、month and day:"); / * 提示輸入年、月、日 * /
scanf(" % d,% d,% d",& date1.year,&date1.month,&date1.day); /*提示年、月、日* /
i =date1.year;
leap =0;
if(( i %4= =0)& & (i % 100! =0))leap=1; / * 判斷是否閏年 * /
if(i%400= =0)leap =1;
date1.yearday = date1.day;
for(i=1;i date1.yearday + = month[leap][i];printf("yearday = % d \ n",date1.yearday);
}
通過該例,可以看出結構體變量的成員,即成員變量,可以像一般變量一樣參與各種操作。
7.2.2 結構體變量的初始化
所謂結構體變量的初始化,就是在定義結構體變量的同時,對其成員變量賦初值。結構體變量初始化的一般形式如下:
struct結構體名 結構體變量名={初始數據};
例如:
struct date
{
int year;
int month;
int day;
int yearday;
char monthname[3];
};
struct date date1={1979,10,29,"OCT"};
在對結構體變量初始化時,應注意如下幾點:
?隻能對外部和靜態結構體變量初始化,不能對auto類型的結構體變量初始化。
?初始化數據之間用逗號(,)隔開。
?初始化數據的個數要與成員的個數相同。
?初始化數據的類型要與相應的成員變量的類型一致。
7.3 結構體數組
7.3.1 結構體數組的定義及初始化
1.結構體數組的定義
結構體數組是其元素都是具有相同結構體變量。同一般數組一樣,結構體數組也必須先定義或說明,才能引用。結構體數組定義的一般格式如下:
[存儲類型]struct結構體名 結構體數組名[元素個數][結構體數組名][元素個數],…];
其中“struct結構體名”是已定義過的結構體類型。存儲類型是結構體數組的存儲類型。
2.結構體數組的初始化
一個外部的或靜態的結構體數組在定義的同時可以初始化。其一般格式是在定義之後 緊跟一個用花括號括起來的一組初始數據:
[存儲類型]struct結構體名 結構體數組名[]={初始數據};
其中“struct結構體名”是預先說明的結構體類型。
或:
[存儲類型]struct結構體名
{
結構體成員表列;
}結構體成員表[]={初始數據};
例如,初始化前麵已說明過的keytab[]結構體數組:
struct key keytab[]={{"shift",0},
{"ctrl",0},
{"del",0},
…
{"pause",0}
};
在初始化時,應當使初始數據的個數與結構體數組的元素個數以及每個數組元素的成員個數匹配。為了增強可讀性,最好使每一個數組元素的初始數據都用花括號括起來。
7.3.2 結構體數組的應用舉例
結構體數組適合於處理一組具有相同結構體類型的數據,下麵舉例說明其應用。
【例7-2】統計全年級男、女生人數及1977年到1980年出生的人數(含1977年和1980年)。
main
{
struct stud / * 定義學生登記表一結構體數組 * /
{
char name[30];
char sex;
int year;
};
struct stud grad[300];
int malenumber,femalenumber,count;
char sex[2],year[5];
int i;
countm = countf = count78 = 0; / * 建立學生名冊 * /
for(i =0;i <50;i + +)
{
readline(grade[i].name,sex,year);
grade[i].sex = sex[0];
grade[i].year = atoi(year);
}
for(i=0;i <300;i+ +) / * 統計男女生人數和1977年到1980年出生人數* /
{
if((grade[i].year> =1977)& &(gradep[i].year< =1980))
count ++;
if(grade[i].sex = =' m '||grade[i].sex = =' m ')
malenumber + +;
else
femalenumber + ;
}
printf(" \ n male: % d",countm); / * 輸出統計結果* /
printf(" \ n女生人數:% d",femalenumber);
printf(" \ n1977年到1980年出生的人數:% d",count);
}
readlin(pname,* psex, * pyear) / * 讀記錄函數 * /
char * pname, * psex, * pyear;
{
printf(" \ nPlease enter name:"); / * 讀入記錄的內容 * /
scanf(" % s",pname);
printf(" \ n Please enter sex:");
scanf(" % s",psex);
printf(" \ n Please enter birth year:");
scanf(" % s",pyear);
}
【例7-3】用結構體數組處理通訊錄。
# include < stdio.h > / * 嵌入頭文件 * /
# include < stdlib.h >
# define MAXIMUM 20 / * 宏定義 * /
struct stud
{
char name[30]; / * 定義結構體 * /
int age;
char sex;
char telnumber[8];
char address[100];
}
main()
{
struct stud student[MAXIMUM]; / * 定義結構體數組student[MAXIMUM]* /
char str[10];
int i;
for(i=0;i {
printf(" \ n Please enter name:");
gets(student[i].name);
printf(" \ n Please enter sex:");
gets(str);
student[i].sex = str[0];
printf(" \ n Please enter age?");
gets(str);
student[i].age = atoi(s);
printf(" \ n Please enter telnumber:");
gets(student[i].telnumber);
printf(" \ n Please enter address:");
gets(student[i].address);
}
printf(" \ n name age sex telnumber address \ n");
printf(" \ n");
for(i =0; i {
printf(" % -14s %-7d",student[i].name,student[i].age); /*輸出結果 * /
printf(" % -7c %-10s %-25s",student[i].age,student[i].telnumber,student[i].address);
}
}
從該例中可以看出,使用結構體數組可以使用循環,使程序十分簡練。
7.4 結構體指針
7.4.1 結構體指針及其定義
結構體指針即指向結構體變量的指針,它是一個指針變量,而且其目標變量是一個結構體變量,其內容是結構體變量的首地址。結構體指針的定義類似一般指針,其一般格式如下:
[存儲類型]struct結構體名 *結構體指針名;
例如:
static struct example *pexample;
其中“struct example”是預先說明的結構體類型。它定義了一個結構體指針pexample,其存儲類型是static。在定義結構體指針變量時,應注意結構體名必須是已說明過的結構體;而且結構體指針在使用之前必須通過初始化賦值操作,以便把某個結構體變量的首地址賦給它,使它指向該結構體變量,結構體指針所指向的結構體變量必須與定義時所規定的結構體類型一致。對於結構體指針的運算,其原則與一般指針相同。可對它進行加1或減1等運算,其含義就是將指針指向下一個或上一個結構體變量。例如: