第6章构造数据类型结构体类型联合体枚举类型用typedef定义类型C语言提供了一种聚合数据类型——结构(或结构体,structure),它是由若干相互关联的数据项构成的数据集合。结构体中所含成员的数量和大小必须是确定的,即结构体不能随机改变大小。组成一个结构体的诸成员的类型可以不同,即结构体是异质的。联合(或共用体,union)类型是一种“可变身份”的数据类型,可在不同的时候在同一存储单元里存放不同类型的数据。6.1结构体类型6.1.1结构体类型的定义一般形式:structstudent{charname[20];intorder,id;intage;charaddress[100];intscore[10];};struct结构体类型名{成员列表;};定义结构体类型时应注意以下几点:①结构体成员可以是任何基本数据类型的变量,如int、char、float和double型等,这些成员的类型可以相同,但往往是不同的。②结构体成员也可以是数组、指针类型的变量。例如:structclist{intcount;char*first;char*last;};③结构体类型可以嵌套定义,即允许一个结构体中的一个或多个成员是其他结构体类型的变量。structid_card{charname[30];charsex;charnationality[20];structdate{intyear,month,day;}birthday;char*p_addr;structdatesigned_date;longintnumber;char*office;};structdate{intyear,month,day;};structid_card{charname[30];charsex;charnationality[20];structdatebirthday;char*p_addr;structdatesigned_date;longintnumber;char*office;};也可以采用另一种形式把各个结构体类型单独定义。④结构体类型定义不允许递归,即:一个结构体类型的成员中不能含有类型为本结构体的变量。例如下面的说明是非法的:structwrong{charname[5];intcount;structwronga;structwrongb;};⑤在同一结构体内各成员的名称不能相同。⑥}后的分号不能省略。6.1.2结构体变量的定义为了使用结构体变量,必须先定义结构体变量。定义结构体变量常用的方法有以下三种:①间接定义:先定义结构体类型,再定义结构体变量。structcoord{floatx;floaty;};structcoordfirst,second;注意,利用此种方法定义结构变量时,关键字struct和结构名必须同时出现,缺一不可。②直接定义:在定义结构体类型的同时定义结构体变量。structcoord{floatx,y;}first,second;③无名定义:当结构体类型和变量同时定义时,可缺省类型名。其一般形式:struct{成员表;}变量名表;例如:struct{floatx,y;}first,second;结构体变量的内部表示定义结构体类型只是声明一种数据类型的“样板”。这种样板是抽象的,还没有实体,不占用内存空间,不能在程序中对结构类型直接进行赋值、存取等操作。结构体变量是实体,占用内存空间,可进行赋值、存取等操作。结构体变量的存储单元依据各个成员在结构中出现的先后次序来分配空间。结构体变量所占空间的大小为结构体中每个成员所占内存空间之和。结构体变量可以嵌套:structrectangle{structcoordtopleft;structcoordbottomrt;}mybox;6.1.3结构体变量的初始化1.结构体变量初始化的一般方式结构体类型结构体变量名={初值表};structstudentwho={Zhanghua,991105,92,91,89,87,94,0.0};2.有聚合成员的结构体变量的初始化为了使层次清晰、增加程序的可读性,往往用花括号将这些聚合类型变量对应的初值整体上括起来。例如,structstudentwho={Zhanghua,991105,{92,91,89,87,94},0.0};注意:①允许初值赋的个数少于结构体变量中成员的个数,但不允许初值符的个数多于结构体变量中成员的个数。structstudentwho={Zhanghua,991105,{92,91,89,87,94}};结构体变量who中成员average就隐含地被初始化为0。②结构变量初始化时,不能在结构体内初始化。③初始化数据的顺序和类型应与定义结构体类型时结构成员的顺序和类型一致。6.1.4结构体变量的成员的访问1.访问结构体成员的一般方式结构体变量名.成员名first.x表示结构变量first的成员x;first.y表示结构变量first的成员y。structcoord{floatx,y;};structcoordfirst;first.x=1.5;first.y=3.8;访问结构成员的方式是“由整体到局部”,即首先指明是哪个结构体变量,然后通过成员访问运算符“.”找到其中指定的成员。结构体变量中成员可以像相同类型的简单变量那样进行相应的运算。在某些情况下允许对结构体变量进行整体操作,如结构体变量的赋值等。但在对结构体变量进行输入和输出时,只能是针对每一个成员进行。2.嵌套结构体中成员的访问如果要访问最内层结构体变量的成员,就要连续的使用“·”运算符,即从外层结构体变量找到内层结构体变量,逐层访问,直至最内层的成员。例如有如下定义:structcoord{floatx,y;};structrectangle{structcoordtopleft;structcoordbottomrt;}mybox;为了对表示矩形的mybox变量的两个坐标点赋值,就要采用如下所示的方式:mybox.topleft.x=1.8;mybox.topleft.y=8.3;mybox.bottomrt.x=12.4;mybox.bottomrt.y=1.29;6.1.5结构体数组1.结构体数组的定义由同一结构体类型的变量组成的数组就称为结构体数组。structscore{intscores[5];//成绩floatav;//平均分};structstudent{charname[20];//姓名longintino;//学号structscorest;//成绩单intorder;//名次}cla[40];2.结构体数组元素的成员的访问如果要访问某个同学的名次,可采用下述形式:cla[i].order如果要访问第i位同学的平均分,则相应表达式为:cla[i].st.av如果要访问第i位同学的第j门功课的成绩,则相应表达式为:cla[i].st.scores[j]3.结构体数组在内存中的表示结构体数组被定义以后,系统为它分配一片连续的内存空间。各数组元素是连续存放的。4.对结构体数组的操作①可以把一个数组元素赋予另一个数组元素,从而实现结构体变量之间的整体赋值。cla[5]=cla[1];②可以单独地把一个结构数组元素中的一个成员的值赋予另一数组元素中的一个同类型的成员。cla[5].ino=cla[1].ino;③结构数组可以初始化。常用的初始化格式有两种:struct结构体类型名{成员列表;}数组名[大小]={初值表};struct结构体类型名{成员表;};struct结构名数组名[大小]={初值表};初值表的形式:{{…},{…},…,{…}}1.指向结构体变量的指针变量结构体指针变量的定义及初始化struct结构体类型名*指针变量名;例如:structstudent1x1;structstudent1*p;p=&x1;6.1.6结构体指针用结构体指针变量访问结构体成员①使用指针运算符“*”(*结构体指针变量名).成员名例如:structstudent1x1,*p;p=&x1;(*p).name=“Zhangsan”②使用间接成员访问运算符“-”结构体指针变量名->成员名例如:p-name=“Zhangsan”小括号不能省略访问结构体变量中的成员的三种方式:结构体变量名.成员名(*结构体指针变量名).成员名结构体指针变量名->成员名2.指向结构体数组的指针变量structperson{charname[20];charsex;intage;};structpersongroup[20];structperson*p;p=&group[0];//p=group;……coutp->namep->sexp->age;例:试分析下面程序的运行结果。#include<iostream.h>structtemp{char*s;inti;structtemp*tp;};voidmain(){staticstructtempa[]={{abcd,1,a+1},{efgh,2,a+2},{ijkl,3,a}};structtemp*p=a;sitpsitpsitpa[0]a[1]a[2]aa+1a+2abcd\0efgh\0ijkl\0123a+1a+2a+3pinti;couta[0].s“\t”p->s“\t”a[2].tp->sendl;for(i=0;i<2;i++){cout--a[i].i“\t”;couta[i].s[3]endl;}cout++(p->s)“\t”;couta[(++p)->i].s“\t”;couta[--(p->tp->i)].sendl;}程序运行结果如下:abcdabcdabcd0d1hbceefgiijkl1.用结构体指针变量作函数的参数例:编写mul函数,求两个复数的乘积。以结构体指针作为参数,函数返回值为结构体类型。6.1.7结构体指针的应用#includeiostream.hstructcomplex{intr;inti;};complexcx={2,3};complexcy={4,5};complexmul(complex*,complex*);voidmain(){complex*px=&cx,*py=&cy;complexpp;coutcx=px-r+px-iiendl;coutcy=py-r+py-iiendl;pp=mul(px,py);coutpp=pp.r+pp.iiendl;}complexmul(complex*pa,complex*pb){complext;t.r=pa-r*pb-r-pa-i*pb-i;t.i=pa-r*pb-i+pa-i*pb-r;returnt;}运行结果:cx=2+3icy=4+5iPp=-7+22i2.用结构体指针处理链表链表概述链表是一种常见的重要的数据结构,是动态地进行存储分配的一种结构。链表的组成头指针:存放一个地址,该地址指向一个元素结点:用户需要的实际数据和链接结点的指针§图11-10用结构体描述链表结点structstudent{intnum;doublescore;structstudent*next;};§图11-11NULL动态存储分配的两个运算符(1)运算符new使用形式:指针变量=new数据类型例如:int*p;p=newint;作用:用于向系统申请动态存储空间,并把首地址作为运算结果。当申请空间失败时,则返回0,即空指针。说明:a.int*p=newint(10);可用于初始化此内存的初始值。b.int*p=newint[10];向系统申请一个有10个数组元素的整型数组。此种情况不能对数组初始化。c.使用new申请得到的空间必须使用delete进行释放。(2)运算符delete使用形式:delete指针变量例如:int*p=