第10章结构体及共用体.docx
- 文档编号:25704374
- 上传时间:2023-06-11
- 格式:DOCX
- 页数:26
- 大小:36.81KB
第10章结构体及共用体.docx
《第10章结构体及共用体.docx》由会员分享,可在线阅读,更多相关《第10章结构体及共用体.docx(26页珍藏版)》请在冰豆网上搜索。
第10章结构体及共用体
第10章结构体与共用体
考核知识点
●结构体与共用体类型数据的定义方法和引用方法
●用指针和结构体构成链表,单向链表的建立、输出、插入与删除
重要考点
●对结构体的声明及定义
●访问结构体的成员的方法
●熟悉对链表的操作
10.1用typedef说明一种新类型名
C语言规定可以用typedef说明一种新的类型名,说明新类型名的语句一般形式为:
typedef类型名称标识符;
其中,“类型名”一定是在此语句之前已有定义的类型标识符。
“标识符”是一个用户定义标识符,用来标识新的类型名。
typedef语句的作用仅仅是用“标识符”来代表已存在的“类型名”,并没有产生新的数据类型,因此,原有的类型名依然有效。
提示:
声明一个新的类型名的具体步骤如下:
⑴先按定义变量的方法写出定义的主体(如floata;).
⑵将变量名换成新类型名(如将a换成FLO)。
⑶在最左面加上关键字typedef(如typedefFLO).
⑷然后可以用新类型名去定义其他的变量(如FLOb;).
10.2结构体类型
在实际工作中,当我们需要把一些不同类型,但相互之间又存在着联系的信息组合应用时,就要用到结构体。
结构体是一种看似复杂却非常灵活的构造数据类型。
在通常情况下,一个结构体类型由若干个称为成员(或称为域)的部分组成。
不同的结构体类型可根据需要由不同的成员组成。
但对于某个具体的结构体类型,其成员的数量必须固定,这一点与数组相同;但结构体中各个成员的类型可以不同,这是结构体与数组的重要区别。
例如,我们常用的“时间”可以由以下3个部分描述:
小时(hour)、分(minute)、秒(second)。
它们都可以用整型数表示,可以把这3个成员组成一个整体,并给它取名为time,这就是一个简单的结构体。
声明一个结构体类型的一般形式为:
struct结构体名
{成员表列};
struct是C语言中的关键字,是结构体类型的标志。
“结构体名”用做结构体类型的标志,它又称“结构体标记”(structure)。
大括号内是该结构体中各成员,成员表列是由若干个变量类型名及变量名组成的。
这些成员共同组成一个结构体。
例如,上面提到的“时间”结构体类型可以说明如下:
structtime
{
inthour;
intminute;
intsecond;
};
其中,time就是结构体名,hour、minute、second都是成员,并且都应进行类型声明,每个成员也就是结构体中的一个域。
成员的命名规则与变量名相同。
所以结构体类型刀可以用以下形式说明:
struct结构体标识名
{
类型名1结构体成员名表1;
类型名2结构体成员名表2;
...
类型名n结构体成员名表n;
}
说明:
⑴“结构体标识名”和“结构体成员名表”都必须是合法的用户定义的标识符。
⑵每个“结构体成员名表”中都可以含有多个同类型的成员名,它们之间以逗号分隔。
⑶结构体类型说明中的“类型名1”~“类型名n”,不仅可以是简单数据类型也可以是某种结构体类型。
当结构体说明中又包含结构体时,称为结构体的嵌套。
⑷ANSIC标准规定结构体至多允许嵌套15层,并且允许内嵌结构体成员的名字与外层成员的名字相同。
10.3结构体类型变量的定义
前面只是指定了一个结构体类型,为了能在程序中使用结构体类型的数据,就需要定义结构体类型的变量,并在其中存放具体的数据。
可以用如下方法定义结构体类型变量。
一、先声明结构体类型再定义变量名
如上面已经定义了一个结构体类型structtime,可以如下定义:
structtimetime1,time2;
结构体类型名结构体变量名;
time1和time2为structtime类型变量,即它们都具有structtime类型的结构。
二、在声明类型的同时定义变量
其一般形式为:
struct结构体名{成员类型}变量名表列;
三、直接定义结构体类型变量
其一般形式为:
struct{成员表列}变量名表列;
即不出现结构体名。
提示:
类型与变量是两个不同的概念,使用时应注意区别。
只能对变量赋值、存取或运算,而不能对一个类型进行赋值、存取或运算。
可以单独使用结构体中的成员,它与普通变量的作用相同。
10.4结构体变量的引用
在定义了结构体变量以后,当然可以引用这个变量。
但应注意:
1.结构体变量不能作为一个整体而对其进行任何操作,只能对结构体变量中的各个成员分别进行输入和输出等操作。
结构体变量中的成员用以下方式引用:
结构体变量名.成员名
2.如果结构体的某个成员本身又是一个结构体类型,则可以使用若干个成员运算符“.”,一级一级地找到最低的一级成员,只能对最低一级成员进行赋值或存取及运算。
3.结构体变量的初始化,是指逐个对结体变量的各个成员进行初始化的过程。
10.5结构体数组
和普通数组一样,结构体数组中和每个元素都属于同一数据类型(结构体类型),只不过各个元素本身又都包含多个成员项,例如,一个结构体变量中存放着一组数据(如某个产品的名称、型号、尺寸、颜色等数据),现在如果有10个这样产品的数据需要参加运算,显然应当用到结构体数组。
和定义结构体变量的方法相仿,只需说明其为数组即可。
其一般形式为:
struct结构体变量名{成员表列}数组名[常量表达式];
结构体数组的初始值应顺序地放在一对花括号中,由于数组中的每个元素都是一个结构体,因此通常将成员的值依次放在一对花括号中,以便区分各个元素。
10.6指向结构体类型数据的指针
一个结构体变量的指针就是用来指向该结构体类型的存储单元,并指向结构体变量所占据的内存段的起始地址。
一、指向结构体变量的指针
看下面的例子:
#include
#include
main()
{
structobjects{
charname[20];
intsize;
charcolor[10];
floatweight;
floatheight;
};
structobjectsobj1;
structobjects*p;
p=&obj1;
strcpy(obj1.name,”pen”);
obj1.size=10;
strcpy(obj1.colur,”black”);
obj1.weight=50.5;
obj1.height=18.5;
printf(“name:
%s\nsize:
%d\ncolor:
%s\nweight:
%f\nheight:
%f\n”,obj1.name,obj1.size,obj1.weight,obj1.height);
printf(“name:
%s\nsize:
%d\ncolor:
%s\nweight:
%f\nheight:
%f\n”,(*p).name,(*p).size,(*p).weight,(*p).height);
}
我们声明了一个structobjects类型,并且定义了一个该类型的变量obj1,又定义了一个指向structobjects类型的数据指针p,并且将p指向obj1,接下来是对各成员赋值。
第一个printf语句用“.”的方式将obj1的成员的值输出。
第二个printf语句用(*p)将obj1的成员的值输出,因为成员运算符“.”的优先级高于“*”运算符,所以(*p)的两侧的圆括号不能省略。
以上两个printf函数语句的输出结果是相同的。
我们可用p->name来代替(*p).name,其中“—>”称为指向运算符,它由两部分组成:
“-”减号和“>”大于号,它们之间不能有空格,所以“结构体变量.成员名”、“*结构体指针变量名.成员名”和“结构体指针变量名—>成员名”这3种形式是等价的。
二、指向结构体数组的指针
结构体数组及其元素也可以用指针变量来指向。
在使用指针变量指向结构体数组时,只要把该结构体数组中的每个元素当做普通的结构体变量使用就可以了。
例如:
#include
structobjects
{
charname[20];
intsize;
charcolor[10];
floatweight;
floatheight;
};
structobjects
obj[3]={{“pen”,10,”black”,50.5,18.5},
{“notebook”,20,”blue,180,19.5”},
{“bag”,50,”red”,2000,37.5}};
main()
{structobjects*p;
printf(“namesizecolorweightheight\n”);
for(p=obj;p printf(“%10s%d%-20s$6.5f%6.5f\n”,p->name,p->size,p->color,p->weight,p->height); } 这样就可以利用指针变量来逐个把结构体数组中的元素的各个域输出。 说明: 如果p的初值为obj,即指向第一个元素,则p+1就指向下一个元素。 例如: (++p)->name;先使p自加1,然后得到它指向的元素中的name成员值。 而(p++)->name;先得到p->name的值,然后使p自加1,指向obj[1];。 p只能指向一个structobjects类型的数据,不能指向obj数组元素中的某一成员(即p的地址不是成员的地址)。 例如,p=&obj[1].name;是不对的。 对结构体变量中的每个成员,都可以像普通变量一样,对它进行同类变量所允许的任何操作。 三、用结构体变量和指向结构体的指针用为函数参数 将一个结构体变量的值传递给另一个函数,有如下方法: 1.结构体变量的成员作为实参传递给主调函数。 2.可以用结构体变量作为一个整体实参 3.C语言,允许将结构体变量的地址作为实参传递,这时,对应的应该是一个基类型相同的结构体类型的指针。 10.7链表 链表是一种常见的重要的数据结构,它是动态地进行存储单元分配的一种结构。 如图10-1所示是一种简单的链表。 由示意图可以乍出链表中的各元素在内趣中不一定是连续存放的。 要找链表中某一元素,必须先找到上一个元素,根据该元素提供的下一元素的地址才能找到下一个元素。 所以,如果没有头指针(head),则整个链表都无法访问。 另外一点,这种链表的数据结构,必须利用指针变量才能实现。 即一个节点中应包含一个指针变量,用它存放下一节点的地址。 当然也可以不通过指针变量,用其他方式也可以构建简单莲表。 下面通过一个例子来说明如何建立和输出一个简单链表。 #include #include structnode{ intdata; structnode*next; } typedefstructnodeNODETYPE; main() { NODETYPEs1,s2,s3,*begin,*p; s1.data=100; s2.tata=200; s3.data=300; begin=&s1; s1.next=&s2; s2.next=&s3; s3.next=’\0’; p=begin; while(p) { printf(“%d”,p->data); p=p->next; } printf(“\n”); } main()函数中这定义的变量s1,s2,s3都是结构体变量,它们都含有data和next两个成员。 变量begin和p是指向NODETYPE结构体类型的指针变量,它们与结构体变量s1\s2\s3中的成员变量next类型相同。 执行赋值语句后,begin中存放s1变量的地址,变量s1的成员s1.next中存放变量s2的地址......最后一个变量s3的成员s3.next置成‘\0’(NULL),从而把同一类型的结构体变量s1、s2、s3“链接”到一起,形成“链表”。 在上例中,链接到一起的每个节点(结构体变量s1、s2、s3)都是通过定义,由系统在内在中开辟了固定的存储单元(不一定连续)。 在程序执行的过程中,不可能人为地再产生新的存储单元,也不可能人为地使已开辟的存储单元消失。 从这一角度出发,可称这种链表为“静态链表”。 在实际中,使用更广泛的是一种“动态链表”。 建立动态链表(主要针对单向链表) 建立单向链表的主要步骤如下: 1.读取数据。 2.生成新节点。 3.将数据存入节点的成员变量中。 4.将新节点插入到链表中,重复上述直至输入结束。 编写函数crealist(),建立带有头节点的单向链表。 节点数据域的数值从键盘输入,以-1作为输入结束标志。 链表的头节点的地址由函数值返回。 我们在函数中定义了一个名为begin的指针变量,用于存放头节点的地址,另外还定义了两个工作指针: current和end。 其中指针current用来指向新生成的节点,指针end总是指向链表当前的尾节点。 每当把current所指的新开辟的节点连接到表尾后,end便移向这一新的表尾节点。 这时又可以用current去指向下一个新开辟的节点。 链表最后一个节点的指针域中置‘\0’(NULL)作为单向链表的结束标志。 链表建成后,头节点的地址由creatlist()返回,赋给main()函数中的指针变量head。 函数如下: structnode{ intdata; structnode*next; }; typedefnodeNODETYPE; NODETYPE*creatlist() {inti; NODETYPE*begin,*end,*current; begin=(NODETYPE*)malloc(sizeof(NODETYPE)); end=begin; scanf(“%d”,&i); while(i! =-1) { current=(NODETYPE*)malloc(sizeof(NODETYPE)); current->data=i; end=current; scanf(“%d”,&i); } end->next=’\0’; returnbegin; } main() {NODETYPE*head; ...... headp=creatlist(); } 以上creatlist()函数中,当一开始输入-1时,并不进入while循环,而直接执行循环之后的end->next=’\0’;语句,这时建立的是一个“空链表”。 由此可见,可用条件begin->next==’\0’来判断链表是否为空。 所谓“访问”,可以理解为取各节点的数据域中的值进行各种运算、修改各节点的数据域中的值等一系列的操作。 输出单向链表各节点数据域中内容的算法比较简单,只需利用一个工作指针(p),从头到尾依次指向链表中的每个节点,当指针指向某个节点时,就输出该节点数据域中的内容,直到遇到链表结束标志为止。 如果是空链表,就只输出提示信息并返回调用函数。 函数如下: voidprintlist(NODETYPE*head) { BODETYPE*p; p=head->next; if(p==’\0’) printf(“Linklistisnull\n”); else{ printf(“head”); do{ printf(“->%d”,p->data); p=o->next;}while(p! =’\0’); } printf(“->end\n”); } 四、在链表中插入节点 在单向链表中插入节点,首先要确定插入节点在指针p所指的节点之前称为“前插”,插入节点在指针p所指向的节点之后称为“后插”。 “前插”操作中各指针的指向如图10-2所示。 当进行前插操作时,需要3个工作指针: s指向新开辟的节点,用p指向插入的位置,q指向要插入的前趋节点。 五、删除链表中的节点 为了删除单向链表中的某个节点,首先要找到待删除的节点(即当前要删除节点的前面一个节点),然后将此前趋节点的指针域去指向待删除节点的后续节点(即当前要删除节点的下一个节点),最后释放被删除节点所占的存储空间即可。 10.8共用体 共用体的类型说明和变量的定义方式与结构体的类型说明和变量定义的方式完全相同。 不同的是,结构体中的成员各自占有自己的存储空间。 而共用体的变量中所有成员占有同一个存储空间。 可以把一个整型变量、一个字符型变量、一个实型变量放在同一个地址开始的内存单元中。 以上3个变量在内存中所占的字节数不同,但都从同一个起始地址开始存放,也就是使用覆盖技术,几个变量相互覆盖。 一、共用体类型的说明 共用体类型说明的一般形式为: union共用体标识名 { 类型名1共用体成员名1; 类型名2共用体成员名2; ... 类型名n共用体成员名n; } 例如: unionexample{ inta; floatb; charc; } 其中,union是关键字,是共用体类型的标志,example是共用体标识名。 “共用体标识名”和“共用体成员名”都是由用户定义的合法标识符,按语法规定共用体标识名是可选项,在说明中可以不出现。 二、共用体变量的定义 和结构体相似,共用体变量的定义也可采用3种方式,一种方法如下: unionun{ inti; floatx; }s1,s2,*p; 说明: 1.共用体变量在定义的同时只能用第一个成员的类型的值进行初始化。 2.“共用体”与“结构体”的定义形式相似,但它们的含义是不同的。 结构体变量所占内存长度是各成员占的内在长度之和,每个成员分别占有其自己的内在单元,而共用体所占的内存长度等于变量中所占字节的成员的长度。 例如,上面的共用体占4字节(因为一个实型变量占4字节)。 三、共用体变量中成员的引用 共用体中每个成员的引用方式与结构体完全相同,可以使用以下3种形式之一: 1.共用体变量名.成员名 2.指针变量名->成员名 3.(*指针变量名).成员名 共用体中的成员变量同样可参与其所属类型允许的任何操作,但在访问共用体成员时应注意: 共用体变量中起作用的是最好近一次存入的面员变量的值,原有成员变量的值将被覆盖。 另外,ANSIC标准允许在两个类型机同的共用体变量之间进行赋值操作。 同结构体一样,共用体类型的变量可以作为实参进行传递,也可以传递共用体变量的地址。 一、选择题: 1.若有以下结构体定义: structecample {intx;inty;}v1; 则()是正确的引用或定义。 A.example.x=0;B.examplev2;v2.x=10; C.structv2;v2.x=10;D.structexamplev2={10}; 2.下述程序的执行结果是() #include unionun { inti; charc[2];} main() { unionunx; x.c[0]=10; x.c[1]=1; printf(“\n%d”,x,i);} A.266B.11C.265D.138 3.根据以下定义能输出字母M的语句是() structperson{charname[9];intage;}; structpersonclass[10]={“John”,17,”paul”,19,”Mary”,18,”Adam”,16}; A.printf(“%c\n”,class[3].name);B.printf(“%c\n”,class[3].name[1]); C.printf(“%c\n”,class[2].name[1]);D.printf(“%c\n”,class[2].name[0]); 4.以下程序的输出是() #include main() { structcmplx{intx;inty;}cnum[2]={1,3,2,7}; printf(“%d\n”,cnum[0].y/cnum[0].x*cnum[1].x); } A.0B.1C.3D.6 5.已知字符0的ASCII代码值的十进制数是48,以下程序 #include main() {union {inti[2]; longk; charc[4];}r,*s=&r; s->i[0]=0x39;s->i[1]=0x38; printf(“%x\n”,s->c[0]);} 其输出结果是() A.39B.9C.38D.8 6.以下程序的输出结果是() #include typedefunion{longx[2]; inty[4]; charx[8]; }MYTYPE; MYTYPEthem; main() { printf(“%d\n”,sizeof(them)); } A.32B.6C.8D.24 7.有以下结构体说明和变量的定义,如图10-4所示,指针p指向变量a,指针q指向变 量b。 则不能把节点b连接到节点a之后的语句是() data next data next a 5 b 9 ↑p ↑q 图10-4结构体说明和变量的定义 structnode { chardata; structnode*next; }a,b,*p=&a,*q=&b; A.a.next=q;B.p.next=&b;C.p->next=&b;D.(*p).next=q; 8.下列程序中,结构体变量a所占内存字节数是() unionU { charst[4]; inti; longl; }; structA { intc; unionUu; }a; A.4B.5C.6D.8 9.设有以下说明语句: structex {intx;floaty;charx;}example; 则下面的叙述中不正确的是() A.struct是结构体类型的关键字B.example是结构体类型名 C.x,y,z都是结体体成员名D.struct是结构体类型名 10.假定建议了以下链表结构,指针p、q分别指向如图10-5所示的节点,则以下可以将q所指节点从链表中删除并释放该节点的语句组是() data next head 8 4 3 ↑p ↑q A.free(q);p->next=q->next;B.(*p).next=(*q).next;free(q); C.q=(*q).next;(*p).next=q;free(q);D.q=q->next;p->next=q;p==->next;free(p); 11.若有下面的说明和定义: structtest { intml;charm2;floatm3; unionuu{charul[5];intu2[2];}ua; }myaa; 则sizeof(structtest)的值是(
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第10章 结构体及共用体 10 结构 共用