7.1 结构型和结构变量
1.结构型
结构型是由若干独立意义成员组成的复杂数据。定义一个结构型的一般形式为
struct 结构型名{
数据类型1 成员1名;
数据类型2 成员2名;
……
数据类型n 成员n名;
};
其中关键字“struct”引出结构型的定义。用花括号括住结构型的成员说明表,指明组成此结构型全部成员的数据类型和名称。结构型的成员也称为域。如某个系统的学生数据实体用如下结构型来描述:
struct stdType {
int num;/*学号*/
char * name;/*姓名,允许姓名字符串长度可变,按需要申请*/
char sex;/*性别'M' 表示男生,'F'表示女生*/
int age;/*年龄*/
int score;/*成绩*/
char *addr; /*家庭地址,允许地址字符串长度可变,按需要申请*/
} ;
上例定义结构型struct stdType,有六个成员。实际上,凡是相关的若干数据对象都可组合成一个结构,在一个结构名下进行管理。
结构成员的数据类型可以是任何类型,包括前面定义的其它结构型,但是结构不能包含自身,而允许结构中可有这样的指针成员,指向如同定义一样的结构。
2.结构变量
结构型的变量就是结构变量,程序要使用结构数据,需定义结构变量。结构变量也简称结构。定义结构变量有以下几种不同的方法。
(l)先定义结构型,再定义结构变量。如利用前面已定义的结构型struct stdType,以下代码
struct stdType st1,st2,stdArray[200]
定义结构变量st1、st2和结构数组stdArray.
(2) 在定义结构型时,同时定义结构变量。如代码
struct point {/*某绘图程序的坐标类型*/
int x;
int y;
} p1,p2;
定义struct point型变量 p1、p2.
(3) 在定义无名结构型时,同时定义结构变量。如某种形式的结构型只是一次性定义几个变量,可以省略结构型名,直接定义结构变量。如由日、月、年组成的日期结构变量为:
struct
int day; /*日*/
int month;/*月*/
int year;/*年*/
} date1,date2;/*定义两个日期变量datel和date2*/
3.结构变量初始化
在定义结构变量时,可同时给它置初值,称为结构变量初始化。结构变量初始化时,要按其结构型定义中的成员顺序逐一给出各成员的初值。如
struct point2 /* 说明绘图程序的坐标类型*/
int x;
int y;
} p3={ 20,50};
结构变量初始化时,对初值表达式的要求与数组初始化对初值表达式的要求相同。
4.结构变量的引用
结构变量定义后,就可以用结构变量的名引用结构。 ANSI C还允许相同类型的结构变量相互赋值。
5.结构变量成员的引用
引用结构变量成员的标记形式为:
结构变量名。成员名
其中“。” 称为成员运算符。例如,datel.year引用结构变量datel的year成员。因该成员的类型为int型的,可以对它施行任何int型变量可施行的运算。例如,赋值运算datel.year=2000.如结构变量的某成员又是结构,要引用其成员的成员,则继续用成员运算符'.'接上更内居的成员名。如结构变量的某成员是数组,要引用其成员的元素,则继续用数组元素的下标引用结构成员的元素。
结构有多个成员,通常结构成员顺序地被安排在结构变量的内存决中,结构变量的地址是结构所占内存的开始地址,写成
& 结构变量
而结构成员的地址写成
& 结构变量。成员名
6.结构数组
一般地,常用结构描述有复杂数据信息的个体,而用数组描述个体的集合。当数组的元素是结构时,这种数组就称为结构数组。如用结构型描述单个学生,而用结构数组表示一个班的学生。
与定义结构变量的方法相仿,在前述所有定义结构变量的方法中,在变量名之后指定元素个数,就能定义结构数组,也可赋初值。
如同元素为标准数据类型的数组一样,结构数组各元素在内存中也顺序存放,初始化时,逐一给数组的每个元素指定结构初值。
对结构数组元素的访问也是利用数组元素下标的引用方法,若引用结构数组元素的成员,
再用结构成员的引用方法,写成以下形式:
结构数组名[元素下标]. 成员名
即首先是指定数组的元素,再指定结构的成员。
引用结构数组元素成员地址的标记方法为
& 结构数组名[元素下标]. 成员名
引用结构数组元素地址的标记方法为
& 结构数组名[元素下标]
引用结构数组首元素地址的标记方法为
结构数组名
7.结构指针变量
把结构变量s所占据的存储段开始地址赋给能指向该结构的指针变量p,就说指针p指向结构变量s.指针p是一个结构指针变量,简称结构指针。定义结构指针的方法,与定义一般指针变量一样,当类型区分符是结构型时,所定义的指针变量即为结构指针。如代码
struct date *pd, d;
定义结构指针pd和结构变量成其中,指针变量pd能指向类型为struct date的结构。赋值代码 pd=&d,使指针pd指向结构变量d.
由指向结构的指针引用结构成员的标记形式为
结构指针变量名-成员名
其中“ -”称为指向成员运算符。例如,如下代码:
pd-day /* 引用结构变量d的成员day*/
pd_month /*引用结构变量d的成员month */
pd- year /* 引用结构变量d的成员year */
表达式“* 指针变量” 表示指针变量所指对象,所以通过指针引用其所指结构的成员也可写成以下形式:
(*指针变量)。 结构成员名
这里圆括号是必需的,因为运算符“*”的优先级低于运算符“。”,但是几乎不用这种标记方法,习惯都采用指向成员运算符“-”来标记。
8.指向结构数组元素的指针
结构指针变量也可指向结构数组的某个元素。如有定义:
struct stdType std[50], *ps,*p;
代码:
ps=&std[2];
p=std;
使指针ps指向结构std[2],指针p指向结构std[0].
通过指针引用它所指数组元素的成员与指向普通结构一样,如代码 ps- score引用std[2].score;而代码 p- score引用std[0].score.
当结构指针ps指向结构数组的元素std[k] 时,表达式ps+n表示指向结构数组元素 std[k+n].利用指向结构数组元素的指针,引用结构数组元素的成员又有多种表示形式:
(l) 利用结构数组元素的指针引用它所指数组元素的成员:
指针变量-成员名 /* 几乎都这样用 */
指针变量[0]. 成员名 /* 几乎不用 */
( *指针变量)。成员名 /* 几乎不用 */
(2) 利用结构数组元素的指针引用离它所指元素i个元素的成员:
( 指针变量+i)-成员名 /* 常用 */
指针变量[i].成员名 /* 最常用 */
( *(指针变量+i))。成员名 /* 几乎不用 */
以下代码序列实现在结构数组std的前n个元素中找最高分的那个结构,并由指针p指向该结构:
P= std; /* 等价于p=&std[0] */
for(ps=p+1; ps<std+n; ps++)
if(ps- score p- score) p=ps;
「例 7.1」 以下函数 dayofYear()利用月份天数表,已知日、月、年,计算年中的第几天。
int dTbl[][12] ={{31,28,31,30,31,30,31,31,30,31,30,31},/*平年*/
{31,29,31,30,31,30,31,31,30,31,30,31}}; /* 闰年 */
int dayofYear( int d,int m,int y) /*计算年中第几天 */
{ int i, leap, day=d;
leap=(y%4==0 && y%100)||y%400==0;
for( i=0; i<m-1; i++)
day += dTbl[leap][i];
return day;
}
9.在函数间传递结构数据
在函数间传递结构数据主要有以下几种形式:
(l) 共用全局的外部结构变量。
(2) 函数设置结构形式参数。
(3) 函数返回结构值。
(4) 函数设置结构指针形式参数。
以函数 dayofYear()为例,设有以下形式的结构类型 struct date,以该类型的结构为形式参数,改写该函数为
struct date {
int day;
int month;
int year;
int yearDay;
char * monthName;
} date;
int dayofYear(struct date d)
{ int i,leap,day=d.day;
leap =(d.year%4==0 && d.year%l00) ||d.year%400==0;
for( i=0;i<d.month-1;i++)
day += dTbl[leap][i];
return day;
}
调用带结构形式参数的函数,必须提供与形式参数相同类型的结构变量实际参数。主函数中对函数dayofYear()的调用应改写成:
date.yearDay=dayofYear(date);
C语言允许函数返回结构型值,如将函数dayofYear()改为设置struct date类型的形式参数,并返回struct date类型的值。对函数dayofYear()的新的改写如下:
struct date dayofYear(struct date d)
{ int i, leap;
d.yearDay=d.day;
leap=(d. year%4==0 && d.year%100) ||d.year%400==0;
for(i=0;i<d.month-1;i++)
d.yearDay+=dTbl[leap][i];
return d;
}
主函数调用函数dayofYear()把返回的结构值赋给结构变量 date:
date=dayofYear(date) ;
再改写函数dayofYear(),使它的形式参数是以struct date结构指针为形式参数。
void dayofYear(struct date *dp)
{ int i,leap,day =dp- day;
leap =(dp - year%4==0 && dp - year%l00) ||dp -year%400==0;
for(i=0;i< dp - month-1;i++)
day+=dThl[leap][i] ;
dp - yearDay=day;
}
改写后的函数通过指针形式参数引用结构成员,并将计算结果存放在结构的相应成员中,不再返回结果。对该函数的调用方式也需相应地改写成:
dayofYear(&date);
7.2 共用型和共用型变量
1.共用型
在某些特殊应用中,要求某数据对象在程序执行的不同时期能存储不同类型的值。C语言的共用型能满足这个要求。共用型的成员从同一地址开始存储。但任一时刻只存储其中一个成员,由最近放入的内容决定该区域究竟是哪一个成员的值。分配给共用型的存储区域大小,要求至少能存储其中最大一种成员。定义共用型的一般形式为
union 共用型名 {
数据类型1 成员1名;
数据类型2 成员2名;
……
数据类型n 成员n名;
};
例如,下面定义的共用型(union udata) 能存储整型,或字符型,或浮点型的数据:
union udata {
int ival;
char chval;
float fval;
};
2.共用型变目的定义
与结构变量的定义方法一样,共用型变量的定义方法有以下几种:
(1) 先定义共用型,然后定义共用型变量、共用型数组、共用型指针等。
(2) 定义共用型同时定义共用型变量、共用型数组、共用型指针等。
(3) 定义无名共用型同时定义共用型变量、共用型数组、共用型指针等。
3.共用型变目初始化
共用型变量定义时,也可初始化,但只能对共用型中的第一个成员初始化。
4.引用共用型变目的成员
引用共用型变量成员的方法与引用结构变量成员的方法相同。共用型也可出现在结构和数组中,共用型也可包含有结构和数组。引用结构中的共用型或共用型中的结构的书写形式与引用嵌套结构成员的书写形式一样。例如,以下代码
Struct {
char name[30]; /* 标识符 */
int uflag; /* 存于共用型成员中的值的类型 */
union /* 存储变量值 */
{ int ival; /* 当变量为整型时 */
char chval /* 当变量为字符型时 */
float fval; /*当变量为浮点型时 */
} uval;
} symTbl[1000]; /* 变量表 */
定义了一个结构数组symTbl.用symTbl[50].uval.fval引用结构数组symTbl中的第50个结构的共用型成员uval的fval(视其中的共用型为浮点型数据)。
函数的形式参数不能是共用型类型,函数的结果也不能是共用型类型。但指向共用型的指针可以作为函数形式参数,函数也可以返回指向共用型的指针。
7.3 枚举型和枚举型变量
1.枚举型
除数字、文字信息之外,还有专用名称信息,如反映电梯运行状态的有上(UP) ,下(DOWN) ,停(sTOP) ;又如表示星期几的名称等。为提高程序描述问题时的直观性,引入枚举型。程序用枚举方法列举一组标识符作为枚举型的值的集合。当一个变量具有这种枚举型时,它就能取枚举型的标识将值。定义枚举型的一般形式为
enum 枚举型名 {枚举常量1,枚举常量2,……,枚举常量n};
其中enum是枚举型的引导字,枚举型名是标识符,枚举常量也是用户指定的标识符,但它们被程序看作常量,习惯称它们为枚举常量。例如,
enum weekday { SUN, MON, TUE, WED, THU, FRI, SAT};
通常,每个枚举常量都是有意义名称符号,但对程序本身来说,这些枚举常量并不自动代表什么含义。例如,并不因为写成SAT就自动表示“星期六”,不写SAT写成SATDAY或任何其它标识符也是可以的。对于编译系统来说,枚举型中的标识符只是一组互不相同的标识符而已,标识符本身的字面意义只是供阅读程序的人便于理解程序。
为了便于处理枚举型,编译系统将每个枚举常量与一个整数相联系,即枚举常量在内部被视作一个整数,值的大小由它们在枚举型中出现的顺序确定,依次为0,l,2,…。如在上面的定义中,SUN值为0,MON值为1,……,SAT值为6.枚举型变量的值也可输出。例如,
printf(“%d n”, SUN);
将输出整数0.
枚举常量的对应整数也可由程序直接指定。如
enum weekday { SUN= 7, MON= l, TUE, WED, THU, FRI, SAT};
指定SUN为7,MON为1,后面未指定对应整数的枚举常量所代表的整数,则是前一个枚举常量代表的整数加1.所以在上述定义中,TUE为2,……,SAT为6.
因枚举常量代表一个整数,同一枚举型的变量、枚举常量或整数相互间都可以作关系比较。
2.枚举型变量
定义枚举型变量也有以下多种方法:
(l) 先定义枚举型,然后定义枚举型变量、枚举型数组、枚举型指针等。
(2) 定义枚举型同时定义枚举型变量、枚举型数组、枚举型指针等。
(3) 定义无名枚举型同时定义枚举型变量、枚举型数组、枚举型指针等。例如:
enum weekday today, yesterday, tomorrow;
enum { RED, YELLOW, BLUE } color;
定义枚举型 enum weekday的变量 today,yesterday,tomorow;定义枚举型变量 color.例如,
today=SUN; tomorrow = MON;
yesterday =SAT ; color= YELLOW;
使用枚举型,除能命名见名议意的标识符外,对标识符值的内部实现,程序员可以不必考虑。另外,一个变量具有枚举型,还能反映变量值的有限性。枚举型变量常用于循环的控制变量,枚举常量用于多路选择控制的情况。
7.4 用户自定义类型
C语言也提供类型定义外化成为类型命名的机制。让用户定义新的类型,并用这些新类型定义变量。用户自定义类型的方法为:
typedef 类型 用户自定义类型名;
其中类型可以是基本类型,也可以是前面用户自定义的类型,也还可以是任何C语言允许的类型描述,如数组类型、结构型、共用型、枚举型,及各种指针类型。用户自定义类型名是标识符,以后就可用该用户自定义类型名定义相应类型的变量。例如,
typedef int INTEGER;
tyPedef struet {
int num;
char * name;
char sex;
int age;
int score;
} stdType; /*定义结构型stdType */
typedef int INTARRAy[20] ; /* 含 20个整数的数组类型 INTARRAY */
typedef enum { RED, YELLOW, BLUE } COLOR; /* 枚举型COLOR */
typedef char *CHP; /* 定义字符指针类型CHP */
利用以上类型定义,可定义变量如下:
INTEGER X,Y; /* 定义int类型变量 x和 y */
stdType std1,std2; /* 定义两个结构变量 */
INTARRAY v1, v2; /* 定义两个各含20个整数的数组 */
COLOR c1,c2; /* 定义两个枚举变量 */
CHP cp1, cp2; /* 定义字符指针变量cpl和cp2 */
在以上变量定义中,对于结构、枚举等类型,不必再冠相应的类型类别关键字。特别对于数组类型,当有多个数组变量成员类型相同、数组元素个数也相同时,先用typedef定义一个数组类型,然后再定义数组变量就比较方便、简洁。
通常,在组织复杂的程序时,不同源程序文件中用到的同一数据类型,如数组、结构、共用型、指针等,常用外typedef定义来给有关数据类型命名,并将这些类型定义单独放在一个源文件中,凡要用到它们的源文件,就用# include预处理命令将它包含进来。