计算机等级考试二级C辅导材料-结构体与共用体
知识点提示
1.结构体与共用体类型数据的定义方法和引用方法。
2.用指针和结构体构成链表,单向链表的建立、输出、删除与插入
知识点
一.结构体数据类型
1.结构体类型的定义
结构体类型是C中用户自定义类型之一,也称为构造类型。结构体类型由结构体的成员构成,这些成员可以是不同的数据类型,所以,数组可看成结构体类型的特例。同样,在实际中,只能运用结构体类型的变量存储数据,只有变量才有对应的内存单元,结构体变量是结构体类型的实例。要定义结构变量,首先要完成结构体类型的定义。结构体类型定义的形式如下:
struct 结构体类型名
{ 成员定义表列 };
成员定义的形式为:类型名 成员名;
说明:
⑴ 结构体类型名与成员名为用户自定义标识符,同时,在结构体类型定义中不允许对成员进行初始化。
⑵ 结构体成员不能是自身类型的变量,但可以指向自身类型的指针。
⑶ 结构体类型的长度为各成员数据类型长度之和。
2.结构体变量的定义
⑴ 先定义结构体类型,后定义结构体变量
形式:struct 结构体类型名 变量名;
⑵ 在声明类型的同时定义结构体变量
形式:struct 结构体类型名
{ 成员定义表列 }变量名表;
⑶ 直接定义结构体类型变量
形式: struct
{ 成员定义表列 }变量名表;
说明:
⑴ 类型与变量是不同的概念。只能对变量赋值、存取或运算,在编译时,对类型不分配内存空间,只对变量分配内存空间。
⑵ 对结构体变量中的成员可以单独使用,它的作用相当于同类型的变量。对结构体变量除了两变量之间的赋值运算之外,对结构体变量操作的对象是它的成员。
⑶ 结构体的成员,可以另一个结构体变量。不能是自身类型的变量,但可以是指向自身类型的指针变量。
⑷ 成员名可以结构体变量名相同,二者代表不同的对象。
3.结构体变量的引用
对结构体变量,除两同类型结构变量能互相赋值外,其余的操作只能对结构体变量的成员进行。结构体变量成员的引用形式为:结构体变量名.成员名。
对结构体变量的成员可以向普通变量一样进行各种运算及赋值、输入、输出操作。若该成员本身又是结构体类型,则用“成员运算符”逐级到最低的一级成员。
4.结构体变量的初始化
在定义结构体变量时,可以对结构体变量中的成员进行赋初值的操作。形式为(有三种):
结构体类型名 变量名={初值表};
5.结构体数组
由同类型结构体变量,按照连续的规则,所组成的集合,便是结构体数组。结构体数组的元素都是同类型的结构体变量。
⑴ 结构体数组的定义
形式:struct 结构体类型名 数组名[常量表达式];
说明:对结构体数组引用及操作的对象同样是结构体数组中的元素,对其元素的操作,最终是对其成员的操作。引用形式为:数组名[下标].成员名。
⑵ 结构体数组的初始化
结构体数组的初始化,与其它类型数组的初始化完成相同,它的一般形式为在定义数组的后面加上:={初值表列};其中,初值表列也可写为分组的格式。
[例] 根据下面的定义,对相应的输出结果进行
。
struct person
{
char name[9];
int age;
};
struct person class[10]={"John",17,"Paul",19,
"Mary",18,"Adam",16};
A.printf("%s\n",stu[3].name); //结果:Adam
B.printf("%c\n",stu[3].name[2]); //结果:a
C.printf("%d\n",stu[2].age); //结果:18
D.printf("%s\n",(stu[2].name)+1); //结果:ary
6.指向结构体类型数据的指针
1.指向结构体变量的指针
指向结构体指针变量的定义:struct 结构体类型名 *指针变量名;
结构体变量的地址:&结构体变量名
结构变量成员的地址:&结构体变量名.成员名
指向关系的确立:结构体指针变量=结构体变量的指针
结构体变量成员的访问:
·结构体变量名.成员名
·(*结构体的指针变量).成员名
·结构体的指针变量->成员名
说明:运算符“.和->”的优先级都属于成员级,在算符中为最高级别,组合方向为左结合性。
2.指向结构体数组的指针
指向结构体数组的指针,这里指的是元素指针,其定义形式与结构体变量的指针定义形式相同。在确立指向关系时,给它赋值结构体数组的地址。
[例] 对下面程序的输出结果进行分析。
struct st
{
int x;
int *y;
} *p;
int dt[4]={10,20,30};
struct st aa[4]={50,&dt[0],60,&dt[1],70,&dt[2],80,&dt[3]};
void main(int argc,char *argv[])
{
p=aa;
printf("%d,",++p->x);
printf("%d,",(++p)->x);
printf("%d\n",++(*p->y));
}
答案:51,60,21 (注意“++、->、*”这三个算符的优先级)
二.用指针处理链表
1.堆内存单元的分配
堆内存单元的分配,是通过有关库函数的调用申请而获得的。
⑴ malloc函数
原型:void* malloc(unsigned int size)
功能:在堆内存区中分配一个长度为size字节的连续单元,函数值返回基类型为void的起始指针。若函数执行失败(申请失败),则返回一空指针(NULL)。
⑵ calloc函数
原型:void* calloc(unsigned n,unsigned size)
功能:在堆内存区中分配n个长度为size字节的连续单元,函数值返回基类型为void的起始指针。若函数执行失败(申请失败),则返回一空指针(NULL)。
记住:用上述两个函数所分配内存单元的无名,只能得到其起始地址,所以要对这些内存单元进行操作,可借助指针变量来完成。在进行指向关系的确立时,要对void类型的指针作强制类型转换。
⑶ free函数
原型:void free(void *p)
功能:释放由p指向的内存区,并且要求p是调用calloc或malloc函数时的返回值。
[例] 分析下列程序输出结果。
void fun(int **s,int p[2][3])
{
**s=p[1][1];
}
void main(int argc,char *argv[])
{
int a[2][3]={1,3,5,7,9,11},*p;
p=(int*)malloc(sizeof(int));
fun(&p,a);
printf("%d\n",*p);
}
答案:9
2.动态链表基本算法
·节点定义
#define LEN sizeof(struct node)
#include
struct node
{
char name[20];
float score;
struct node *next;
};
·建立动态链表
struct node* creat(void)
{
struct node* head;
struct node *pnew,*pend;
pnew=pend=(struct node*)malloc(LEN);
printf("输入首节点的值:");
scanf("%s%f",pnew->name,&pnew->score);
if(pnew->score<=0)
{
head=NULL;
return head;
}
else
head=pnew;
while(1)
{
pnew=(struct node*)malloc(LEN);
printf("请输入新节点的值:");
scanf("%s%f",pnew->name,&pnew->score);
if(pnew->score<=0) break;
pend->next=pnew;
pend=pnew;
}
pend->next=NULL;
return head;
}
·遍历动态链表
void print(struct node* head)
{
struct node* p;
p=head;
if(head!=NULL)
{
do
{
printf("%s,%f\n",p->name,p->score);
p=p->next;
}while(p!=NULL);
}
}
·删除动态链表中的指定节点
struct node * del(struct node* head,char *name)
{
struct node *p1,*p2;
if(head==NULL)
{
printf("\nlist null!\n");
return head;
}
p1=head;
while(strcmp(name,p1->name)&&p1->next!=NULL)
{
p2=p1;p1=p1->next;
}
if(strcmp(name,p1->name)==0)
{
if(p1==head)
head=p1->next;
else
p2->next=p1->next;
printf("delete:%s\n",name);
}
else
printf("\nNot been found!\n");
return head;
}
·在节点升序链接中插入一节点
struct node* insert(struct node *head,struct node *node1)
{
struct node *p0,*p1,*p2;
p1=head;
p0=node1;
if(head==NULL)
{
head=p0;
p0->next=NULL;
}
else
{
while(strcmp(p0->name,p1->name)>0&&p1->next!=NULL)
{
p2=p1;
p1=p1->next;
}
if(strcmp(p0->name,p1->name)<0)
{
if(head==p1) head=p0;
else p2->next=p0;
p0->next=p1;
}
else
{
p1->next=p0;
p0->next=NULL;
}
}
return head;
}
三.共用体类型
1.共用体类型的概念
共用体类型是指将几个不同类型的数据存放到同一段内存单元中。定义共用体类型及变量的形式与结构体类似,其形式如下:
union 共用体类型名
{
成员表列
}变量名表列;
说明:对于共用体类型及变量的定义还有其它两种形式,即先定义共用体类型,后定义共用体变量;直接定义共用体变量。值得说明的另外一个问题,共用体变量所占内存的长度为其成员中的最大成员的长度。
2.共用体变量的引用
对于共用体变量,其引用的对象只能是共用体变量的成员。引用形式:共用体变量.成员名。
3.共用体类型的特点
⑴ 同一内存单元中可以用来存放不同类型的数据,但在同一时刻,只有一个成员的数据是有效的。也就是共用体变量的值是最后一次给成员存放的值。
⑵ 共用体变量的地址和它各成员的地址是同一地址。
⑶ 共用变量不允许整体赋值,也不允许对共用体变量进行初始化的操作。
⑷ 共用体变量不允许作为函数的参数,同样函数返回值的类型也不能为共用体类型,但可以使用指向共用体变量的指针。
⑸共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。同样,结构类型也可出现在共用体类型定义中,数组也可作为共用体的成员。
[例] 分析下述程序的运行结果。
void main(int argc,char *argv[])
{
union
{
unsigned char c;
unsigned short i[4];
}z;
z.i[0]=0x39;
z.i[1]=0x36;
printf("%c\n",z.c);
}
答案:9。
[例] 设有如下定义:
typedef union
{
long i;
int k[5];
char c;
}DATE;
struct date
{
int cat;
DATE cow;
double dog;
}too;
DATE max;
分析语句:printf("%d\n",sizeof(struct date)+sizeof(max));的输出结果。
答案:30。
四.枚举类型
枚举数据类型可认为整型的子集,实际为离散整型数据的有限集合。枚举数据类型为一次定义多个整型的符号常量提供了途径。
枚举类型的定义格式:enum 枚举类型名 {枚举元素定义列表};
枚举元素的定义格式: 枚举常量标识符[=整常量表达式]
枚举变量的定义:enum 枚举类型名 变量表列;
说明:
⑴ 不能对枚举元素(枚举常量)进行赋值。
⑵ 枚举常量的值在定义时,若指定了值,则就为该指定值。若未指定值,第一个枚举常量的值为0,其余的枚举常量值为前一枚举值加1。
⑶ 枚举型数据作为运算对象或输出时,按整型数据处理。
⑷ 一个整型数据不能直接给枚举变量赋值,必须做强制数据类型的转换。
[例] 分析下述程序的输出结果。
enum team {my,your=4,his,her=his+10};
printf("%d,%d,%d,%d\n",my,your,his,her);
答案:0,4,5,15。
同步训练题
一.选择题
1.若程序中有下面的说明和定义:
struct abc
{
int x; char y;
}
struct abc s1,s2;
则会发生的情况是( )。
A.编译出错
B.程序能顺利编译、连接、执行
C.能顺利通过编译、连接,但不能执行
D.能顺利通过编译,但连接出错
2.根据下面的定义,能打印出字母M的语句是( )。
struct person
{
char name[9];
int age;
};
struct person stu[10]={"John",17,"Paul",19,
"Mary",18,"Adam",16};
A.printf("%s\n",stu[3].name);
B.printf("%c\n",stu[3].name[1]);
C.printf("%c\n",stu[2].name[1]);
D.printf("%c\n",stu[2].name[0]);
3.下面程序的输出是( )。
typedef union
{
long x[2];
int y[4];
char z[8];
}MYTYPE;
MYTYPE them;
void main()
{
printf("%d\n",sizeof(them));
}
A.32 B.16 C.8 D.24
4.若有下面说明和定义,则sizeof(struct aa)的值是( )。
struct aa
{
int r1; double r2; float r3;
union uu
{
char u1[5];
long u2[2];
}ua;
}mya;
A.30 B.29 C.24 D.22
5.若有定义:struct link { char data; struct link *next } *p,*s;且指针p指向链表末尾节点的前一个节点,指针s指向新节点。则不能将s所指向的节点插入到末尾的语句是( )。
A.s->next=NULL; p=p->next; p->next=s;
B.p=p->next; s->next=p->next; p->next=s;
C.p=p->next; s->next=p; p->next=s;
D.p=(*p).next; (*s).next=(*p).next; (*p).next=s;
6.根据下述定义,可以输出字符'A'的语句是( )。
struct person
{
char name[11];
struct
{
char name[11];
int age;
}other[10];
};
struct person man[10]={{"Jone",{"Paul",20}},{"Paul",{"Mary",18}},
{"Mary",{"Adam",23}},{"Adam”,{"Jone",22}}};
A.printf("%c",other[0].name[0]); B.printf("%c",man[2].(*other[0]));
C.printf("%c",man[3].name); D.printf("%c",man[2].other[0].name[0]);
7.下述程序的执行结果是( )
union tt
{
int i; char ch[2];
};
void main()
{
union tt x;
x.ch[0]=10;
x.ch[1]=1;
printf("%d\n",x.i);
}
A.11 B.不确定值 C.110 D.266
8.enum day {mon,tue,wen=4,thu,fri,sat,sum=10};定义了一个枚举类型,编译程序为值表中各标识符分配的枚举值依次为( )。
A.1,2,3,4,5,6,7 B.0,1,2,3,4,5,6
C.0,1,4,5,6,7,10 D.0,1,4,3,4,5,10
9.若有以下结构定义:
struct example
{
int x; int y;
}v1;
则正确的引用或定义是( )。
A.example.x=10; B.example v2; v2.x=10;
C.struct v2; v2.x=10; D.struct example v2={10};
10.若有以下的说明,则正确叙述的是( )。
struct st
{
int a; int b[2];
}a;
A.结构体变量a与结构体成员a同名,定义是非法的
B.程序只在执行到该定义时才为结构体st分配存储单元
C.程序运行时为结构体st分配6字节存储单元
D.类型名struct st可通过extern关键字提前引用
11.对于如下的结构体定义,若对变量person的出生年份进行赋值,正确的是( )。
struct date
{
int year,month,day;
};
struct worklist
{
char name[20];
char sex;
struct date birthday;
}person;
A.year=1976 B.birthday.year=1976
C.person.birthday.year=1976 D.person.year=1976
12.下述程序的输出结果是( )。
void main()
{
struct st
{
int x;
unsigned a:2;
unsigned b:2;
};
printf("%d\n",sizeof(struct st));
}
A.2.5 B.3 C.2 D.6
13.下述语句段中,正确的是( )。
A.struct ex
{
int x;
float y;
unsigned a:2;
unsigned b:3;
char cx[10];
};
B.struct ex
{
unsigned a:3;
unsigned b:4;
}x;
unsigned *p=&x.a;
C.struct s
{
int a;
float x:4;
}y={1,1.0};
float data=y.x;
D.struct nd
{
int a,b:3;
unsigned c[2]:2;
};
14.以下定义变量的程序片段中,含有错误的是( )。
A.定义整型变量x,a和b
typedef int INT;
INT x;
int a,b;
B.定义整型变量a和b
#define INTEGER int
INTEGER a,b;
C.定义字符类型的指针变量x和y
typedef char *CP;
CP *x,*y;
D.定义结构体变量a和b
typedef struct
{
int x,y;
char cx[10];
}st;
st a,b;
15.对于下述说明,不能使变量p->b的值增1的是( )。
struct st
{
int a;
int *b;
}*p;
A.*++p->b B.*++((p++)->b) C.*p->b++ D.(*(p++)->b)++
16.下述程序的运行结果是( )。
struct st
{
int n;
int *m;
}*p;
void main()
{
int d[5]={10,20,30,40,50};
struct st arr[5]={100,d,200,d+1,300,d+2,400,d+3,500,d+4};
p=arr;
printf("%d\t",++p->n);
printf("%d\t",(++p)->n);
printf("%d\n",++(*p->m));
}
A.101 200 21 B.101 20 30
C.200 101 21 D.101 101 10
17.若有下述程序段,则值为6的表达式是( )。
struct st
{
int n;
struct st *next;
};
struct st a[3]={5,&a[1],7,&a[2],9,'\0'},*p=a;
A.p++->n B.p->n++ C.(*p).n++ D.++p->n
18.已建立链表结构,指针p指向被删节点的前一个节点,指针s指向被删节点。则能够将指针s指向的节点从链表中删除并释放该节点的语句组是( )。
struct list
{
int data;
struct list *next;
} *p,*s;
A.free(s);
p->next=s->next;
B.s=s->next;
p->next=s; free(s);
C.p->next=s->next;
free(s);
D.s=s->next; p->next=s;
p=p->next; free(s);
19.对于下述定义中,不正确的叙述是( )。
union data
{
int i;
char c;
float f;
}a,b;
A.变量a所占内存的长度等于成员f的长度
B.变量a的地址和它的各成员地址都是相同的
C.可以在定义时对a初始化
D.不能对变量a赋值,故a=b非法
20.若有如下定义,则能通过编译的是( )。
union data
{
int i;
char c;
float f;
}a;
A.a=5; B.n=a; C.a={2, 'a',1.2}; D.printf("%d",a);
21.执行下述程序语句后的结果是( )。
void main()
{
enum week {sun,mon=3,tue,wed,thu};
enum week day;
day=wed;
printf("%d\n",day+1);
}
A.6 B.5 C.3 D.编译时出错
22.下述程序的输出结果是( )。
void main()
{
struct complex
{
int x,y;
}cnum[2]={1,3,2,7};
printf("%d\n",cnum[0].y/cnum[0].x*cnum[1].x);
}
A.0 B.1 C.3 D.6
23.下述程序的输出结果是( )。
typedef union
{
short x[2];
short y[4];
char z[8];
}TYPE;
void main()
{
static TYPE var;
var.x[0]=sizeof(var)-1;
printf("%d\n",var.z[0]);
}
A.8 B.6 C.7 D.不确定值
24.若有如下的定义和语句,则值为101的表达式是( )。
struct ws
{
int a; int *b;
};
int x0[]={11,12},x1[]={31,32};
struct ws x[2]={100,x0,300,x1};
struct ws *p=x;
A.*p->b B.p->a C.++p->a D.(p++)->a
25.若有以下的说明,则值为2的表达式是( )。
struct
{
char ch; int i; double x;
}arr[2][3]={{'a',1,3.45},{'b',2,7.89},{'c',3,1.93}};
A.arr[0][1].ch B.arr[0][1].i C.arr[0][0].i D.arr[0][2].i
26.下述程序的输出结果是( )。
void main()
{
enum team {Jone,Adam,Smith=10,Bob=Smith+2,Liang};
printf("%d,%d\n",Adam,Liang);
}
A.1,13 B.2,13 C.1,0 D.1,12
二.填空题
1.有以下定义语句:struct st{int day;struct st *next;}a,*p=&a;,可用a.day引用结构体成员day,请写出引用结构体成员a.day的其它两种形式 【1】` , 【2】 。
2.建立的存储结构为:每个节点有两个域,data是数据域,next是指向结点的指针。请填空。
struct link { char data; 【1】 } node;
3.下面min函数的功能是:计算单向循环链表first中每3个相邻节点数据域中的值之和,返回其中最小的值,请填空。
struct node {int data; struct node *link;};
int min(struct node *first)
{
struct node *p=first;
int m,m3=p->data+p->link->data+p->link->link->data;
for(p=p->link; p!=first;p=【1】)
{
m=p->data+p->link->data+p->link->link->data;
if(【2】) m3=m;
}
return m3;
}
4.以下函数creat用来建立一个带头节点的单向链表,新产生的节点总是插在链表的末尾。单向链表的头指针作为函数值返回,请填空。
struct node
{
char data;
struct node *next;
};
struct node* creat(void)
{
struct node *h,*p,*q;
char ch;
h=【1】 malloc(sizeof(struct node ));
p=q=h;
ch=getchar();
while(ch!= '?' )
{
P=【2】 malloc(sizeof(struct node));
p->data=ch;
q->next=p;
ch=getchar();
}
p->next=NULL;
【3】;
}
5.下面的程序调用getone函数开辟一个动态存储单元,调用assone函数把数据输入此动态存储单元,调用outone函数输出此动态存储单元中的数据,请填空。
void getone(int **s)
{
【1】 =(int*)malloc(sizeof(int));
}
void assone(int *a)
{
scanf("%d",【2】);
}
void outone(int *b)
{
printf("%d\n",【3】);
}
void main()
{
int *p;
getone(&p);assone(p);outone(p);
}
6.函数del的功能是删除数据域的值为data的节点,释放其占用空间,并返回表头指针,请填空。
typedef struct node
{
int data;
struct node *next;
}NODE;
NODE *del(NODE *head,int data)
{
NODE *delete,*link;
if(head==NULL)
printf("Empty\n");
else
{
delete=head;
while(data!=delete->data&&delete->next!=NULL)
{
link=【1】;
delete=delete->next;
}
if(data==delete->data)
{
if(delete==head)
head=delete->next;
else
【2】=delete->next;
free(【3】);
}
else
printf("Not found\n");
}
return head;
}
7.在一个链表的节点结构中,有三个域:front是指向前一个节点的指针域,data是指向字符串的指针域,next是指向下一个节点的指针域,请填空完成此节点结构的类型定义和说明。
struct link
{
【1】
【2】
【3】
};
8.函数count(head)统计链表的节点个数,head为表头指针。阅读程序,请填空。
typedef struct node
{
int data;
struct node *next;
}NODE;
int count(NODE *head)
{
int n=0;
if(【1】)
do
{
n++;
head=【2】;
}while(head!=NULL);
return n;
}
9.Head是一个链表的头指针,该链表节点的data域由小到大排序。函数insert(head,data)将一个值为data的新节点插入到链表中,且保证插入后的链表仍然是有序的。请填空。
typedef struct node
{
int data;
struct node *next;
}NODE;
NODE* insert(NODE* head,int data)
{
NODE *new,*front,*current;
current=head;
while((current!=NULL)&&【1】)
{
front=current;
current=current->next;
}
new=【2】;
new->data=data;
new->next=current;
if(【3】)
head=new;
else
front->next=new;
return head;
}
10.函数findmax(head,p)在以head为表头指针的单向链表中查找包含最大数据域值的节点,并由p带回该节点。请填空。
typedef struct node
{
int data;
struct node *next;
}NODE;
void findmax(NODE *head,NODE **P)
{
*p=head;
while(【1】!=0)
{
head=head->next;
if(head->data>【2】)
*p=head;
}
}
void main()
{
NODE *head,*max;
......;
findmax(head,【3】);
printf("%d\n",max->data);}
11.下述程序实现对两数x和y的判定,若
,则计算并输出,否则打印出错信息并继续读数,直到输入正确。请填空。
enum ErrorData {Right,Lesson,Great100,MinMaxErr};
char *ErrorMessage[]={"Enter Data Right","Data<0 Error",
"Data>100 Error","x>y Error"};
int error(int min,int max)
{
if(max100)
return Great100;
if(min<0)
return Lesson;
return 【1】;
}
void main()
{
int status,x,y;
do
{
printf("Please Enter two number:(x,y)");
scanf("%d%d",&x,&y);
status=【2】;
printf(ErrorMessage[【3】]);
}while(status!=Right);
printf("Result=%d\n",x*x+y*y);
}
12.函数sun(head)计算以head为表头指针的单向链表中所有节点数据域值的和并返回计算结果。请填空。
typedef node { int data; struct node *next; }NODE;
int sum(【1】)
{
int s=0;
while(head!=0)
{
s+=【2】;
head=【3】;
}
return s; }
习题解析
1. 选择题
1.答案A。结构体定义缺少分号。
2.答案D。A选项输出字符串“Adam”,B选项输出字母“d”,C选项输出字母“a”。
3.答案C。共用体的类型长度为成员类型中的最大长度。
4.答案D。
5.答案C。
6.答案D。
7.答案D。x.i的对应存储数据为:0000000100001010=256+10=266。
8.答案C。
9.答案D。
10.答案D。
11.答案C。
12.答案B。位段的长度为1。
13.答案A。位段的类型不能为浮点型及数组类型。
14.答案C。
15.答案D。成员运算符优先级高于“*”与“++”算符,且单目算符的结合性为右结合性。
16.答案A。
17.答案D。
18.答案C。
19.答案C。共用体变量不允许初始化操作。
20.答案D。
21.答案A。
22.答案D。
23.答案C。
24.答案C。
25.答案B。
26.答案A。
二.填空题
1.【1】p->day 【2】(*p).day
2.【1】struct link *next
3.
第 20 页 共 21 页
_1154803588.unknown