第十三章 结构体与共用体第十三章 结构体与共用体
13.1 结构体类型变量的定义和引用
结构体类型的定义形式:
struct 结构体名
{
成员项表列
};
第十三章 结构体与共用体
有三种形式定义结构体类型的变量
( 1)先定义结构体类型,再定义结构体类型变量:
struct stu / *定义学生结构体类型 * /
{ char name[20]; / * 学生姓名 * /
char sex; / * 性别 * /
long num; / *学号 * /
float score[3]; / * 三科考试成绩 * /
};
struct stu student1,student2; / * 定义结构体类型变量 * /
struct stu student3,student4;
用此结构体 类型,可以定义更多的该结构体类型变量。
13.1 结构体类型变量的定义和引用一、结构体类型变量的定义第十三章 结构体与共用体
( 2)定义结构体类型同时定义结构体类型变量:
struct data
{ int day;
int month;
int year;
} time1,time2;
也可以再定义如下变量:
struct data time3,time4;
用此结构体类型,同样可以定义更多的该结构体类型变量。
13.1 结构体类型变量的定义和引用一、结构体类型变量的定义第十三章 结构体与共用体
( 3)直接定义结构体类型变量:
struct
{ char name[20]; / *学生姓名 * /
char sex; / *性别 * /
long num; / *学号 * /
float score[3]; / *三科考试成绩 * /
} person1,person2; / *定义该结构体类型变量 * /
该定义方法由于无法记录该结构体类型,所以除直接定义外,不能再定义该结构体类型变量。
13.1 结构体类型变量的定义和引用一、结构体类型变量的定义第十三章 结构体与共用体
引用的形式为,<结构体类型变量名 >,<成员名 >
若定义的结构体类型及变量如下:
struct data
{ int day;
int month;
int year;
} time1,time2;
则变量 time1和 time2各成员的引用形式为:
time1.day,time1.month,time1.year及 time2.day、
time2.month,time2.year。
其结构体类型变量的各成员与相应的简单类型变量使用方法完全相同。
13.1 结构体类型变量的定义和引用二、结构体类型变量的引用第十三章 结构体与共用体
结构体类型变量的定义和初始化为:
struct stu / *定义学生结构体类型 * /
{ char name[20]; / * 学生姓名 * /
char sex; / * 性别 * /
long num; / *学号 * /
float score[3]; / * 三科考试成绩 * /
};
struct stu student={"liping",'f',970541,98.5,
97.4,95};
上述对结构体类型变量的三种定义形式均可在定义时初始化。
13.1 结构体类型变量的定义和引用二、结构体类型变量的初始化和赋值第十三章 结构体与共用体结构体类型变量完成初始化后,即各成员的值分别为:
student.name="liping",student.sex='f'、
student.num=970541,student.score[0]=98.5、
student.score[1]=97.4,student.score[2]=95。
其存储在内存的情况如图:
13.1 结构体类型变量的定义和引用二、结构体类型变量的初始化和赋值
Liping f 970541 98.5 97.4 95
在 ANSI C中允许相同类型的结构变量相互赋值。
第十三章 结构体与共用体
[例 13.1]外部结构变量初始化。
struct stu /*定义结构 */
{ int num;
char *name;
char sex;
float score;
} boy2,boy1={102,"Zhang ping",'M',78.5};
main( )
{ boy2=boy1;
printf("Number=%d\nName=%s\n”,boy2.num,boy2.name);
printf("Sex=%c\nScore=%f\n”,boy2.sex,boy2.score);
}
13.1 结构体类型变量的定义和引用二、结构体类型变量的初始化和赋值第十三章 结构体与共用体
[例 13.2]给结构变量赋值并输出其值。
main( )
{ struct stu
{ int num;
char *name;
char sex;
float score;
} boy1,boy2;
boy1.num=102; boy1.name="Zhang ping";
printf("input sex and score\n");
scanf("%c %f",&boy1.sex,&boy1.score);
boy2=boy1;
printf("Number=%d\nName=%s\n”,boy2.num,boy2.name);
printf("Sex=%c\nScore=%f\n",boy2.sex,boy2.score);
}
13.1 结构体类型变量的定义和引用二、结构体类型变量的初始化和赋值第十三章 结构体与共用体
C允许定义嵌套的结构体类型
struct data struct stu
{ int day; { char name[20];
int mouth; struct data birthday;
int year; long num;
}; } person;
如果成员本身又是一个结构则必须逐级找到最低级的成员才能使用:用若干,.”运算符,逐级引用到最低级。
如,person.name,person.birthday.day、
person.birthday.month,person.birthday.year、
person.num 。
13.1 结构体类型变量的定义和引用二、结构体类型变量的初始化和赋值第十三章 结构体与共用体
结构体类型数组的定义形式为:
struct stu
{ char name[20];
char sex;
long num;
float score[3];
};
struct stu stud[20]; / *该数组有 2 0个结构体类型元素 * /
其数组元素各成员的引用形式为:
stud[0].name,stud[0].sex,stud[0].score[i];
stud[1].name,stud[1].sex,stud[1].score[i];
……
stud[19].name,stud[19].sex,stud[19].score[i];
13.2 结构体数组的定义和引用第十三章 结构体与共用体
[例 13.3]计算学生的平均成绩和不及格的人数。
struct stu
{ int num;
char *name;
char sex;
float score;
}boy[5]={{101,"Li ping”,'M?,45},{102,"Zhang
ping”,'M?,62.5},{103,"He fang”,'F?,92.5},{104,"Cheng
ling”,'F?,87},{105,"Wang ming",'M',58},};
main( )
{ int i,c=0; float ave,s=0;
for(i=0; i<5; i++ )
{ s+ =boy[i].score;
if(boy[i].score<60) c+ =1;
}
printf(“s=%f\n”,s); ave=s/5;
printf("average=%f\ncount=%d\n",ave,c);
}
13.2 结构体数组的定义和引用第十三章 结构体与共用体
已知结构体 stu,定义指向结构体类型变量的指针变量:
struct stu
{ char name[20];
long number;
float score[4];
} stu1;
struct stu *p1,*p2 ;
13.3 结构体指针的定义和引用一、指向结构体类型变量的使用第十三章 结构体与共用体
引用形式:
指针变量名 → 成员名;
当指针真正指向了结构体型变量后,以下三种引用形式是等价的:
1、结构体变量,成员名;
2、( *指针变量名),成员名
3、指针变量名 ->成员名;
13.3 结构体指针的定义和引用一、指向结构体类型变量的使用第十三章 结构体与共用体
[例 13.4]输入学生的基本情况,然后输出。
struct data
{ int day,month,year;
} ;
struct stu
{ char name[20]; long num;
struct data birthday;
} st;
main( )
{ struct stu *student; student=&st;
printf("Input name,number,year,month,day,\n");
scanf(“%s”,student->name); scanf("%ld”,&student->num);
scanf("%d%d%d”,&student->birthday.year,&student-
>birthday.month,&student->birthday.day);
printf("\nOutput name,number,year,month,day\n" );
printf("%20s%10ld%10d//%d//%d\n“,student->name,student-
>num,student->birthday.year,student->birthday.month,student-
>birthday.day);
}
13.3 结构体指针的定义和引用一、指向结构体类型变量的使用第十三章 结构体与共用体
结构指针变量可以指向一个结构数组,
这时结构指针变量的值是整个结构数组的首地址。结构指针变量也可指向结构数组的一个元素,这时结构指针变量的值是该结构数组元素的首地址。设 ps为指向结构数组的指针变量,则 ps也指向该结构数组的 0号元素,ps+ 1指向 1号元素,ps+ i则指向 i号元素。
13.3 结构体指针的定义和引用二、指向结构体类型数组的指针的使用第十三章 结构体与共用体
[例 13.5]用指针变量输出结构数组。
struct stu
{ int num; char *name; char sex; float score;
}boy[5]={{101,"Zhou ping”,'M?,45},{102,"Zhang
ping”,'M?,62.5},{103,"Liou fang”,'F?,92.5},{104,"Cheng
ling”,'F?,87},{105,"Wang ming”,'M?,58}} ;
main( )
{ struct stu *ps;
printf("No\tName\t\t\tSex\tScore\t\n");
for(ps=boy; ps<boy+ 5; ps++)
printf("%d\t%s\t\t%c\t%f\t\n",ps->num,ps-
>name,ps->sex,ps->score);
}
13.3 结构体指针的定义和引用二、指向结构体类型数组的指针的使用第十三章 结构体与共用体
用指针变量作函数参数进行传送,由实参传向形参的只是地址,从而减少了时间和空间的开销。
13.3 结构体指针的定义和引用三、用指向结构体的指针作为函数的参数第十三章 结构体与共用体
[例 13.6]计算一组学生的平均成绩和不及格人数。用结构指针变量作函数参数编程。
struct stu
{ int num;
char *name;
char sex;
float score; }boy[5]={
{101,"Li ping",'M',45},
{102,"Zhang ping",'M',62.5},
{103,"He fang",'F',92.5},
{104,"Cheng ling",'F',87},
{105,"Wang ming",'M',58},
};
13.3 结构体指针的定义和引用三、用指向结构体的指针作为函数的参数第十三章 结构体与共用体
main( )
{ struct stu *ps;
void ave(struct stu *ps);
ps=boy;
ave(ps);
}
void ave(struct stu *ps)
{ int c=0,i;
float ave,s=0;
for(i=0; i<5; i++,ps++ )
{ s+ =ps- >score;
if(ps- >score<60) c+ =1;
}
printf("s=%f\n",s);
ave=s/5;
printf("average=%f\ncount=%d\n",ave,c);
}
13.3 结构体指针的定义和引用三、用指向结构体的指针作为函数的参数第十三章 结构体与共用体
常用的内存管理函数:
1.分配内存空间函数 malloc。
调用形式,(类型说明符 *) malloc (size)
功能:在内存的动态存储区中分配一块长度为 "size" 字节的连续区域。函数的返回值为该区域的首地址。
,类型说明符”表示把该区域用于何种数据类型。
(类型说明符 *)表示把返回值强制转换为该类型指针。
,size”是一个无符号数。
如,pc=(char *) malloc (100);
表示分配 100个字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的指针,把该指针赋予指针变量 pc。
13.4 链表的建立、插入和删除一、单链表第十三章 结构体与共用体
2.分配内存空间函数 calloc
调用形式,(类型说明符 *)calloc(n,size)
calloc 也用于分配内存空间功能:在内存动态存储区中分配 n块长度为,size”字节的连续区域。函数的返回值为该区域的首地址。
(类型说明符 *)用于强制类型转换。
calloc函数与 malloc 函数的区别:在于一次可以分配 n
块区域。
如,ps=(struet stu*) calloc(2,sizeof (struct stu));
其中的 sizeof(struct stu)是求 stu的结构长度。因此该语句的意思是:按 stu的长度分配 2块连续区域,强制转换为 stu类型,并把其首地址赋予指针变量 ps。
13.4 链表的建立、插入和删除一、单链表第十三章 结构体与共用体
3.释放内存空间函数 free
调用形式,free(void*ptr);
功能:释放 ptr所指向的一块内存空间,ptr 是一个任意类型的指针变量,它指向被释放区域的首地址。被释放区应是由 malloc或 calloc函数所分配的区域。
13.4 链表的建立、插入和删除一、单链表第十三章 结构体与共用体
4.用 typedef定义类型
可以用 typedef定义的新的类型名来代替已有的类型名。
如,typedef int INTEGER;
typedef float REAL;
在上面的定义中,指定用 INTEGER代表 int类型,
REAL代表 float。 以下两行等价:
① int i,j; float a,b;
② INTEGER i,j; REAL a,b;
13.4 链表的建立、插入和删除一、单链表第十三章 结构体与共用体
定义一个新的类型名的方法是:
(1)先按定义变量的方法写出定义体(如,int i;)
(2)将变量名换成新类型名(如:将 i换成
COUNT)。
(3)在最前面加 typedef (如,typedef int COUNT)。
(4)然后可以用新类型名去定义变量。
定义一个新的类型名的好处是:书写方便、适应习惯。
说明,typedef 只对原有类型起新的名字,并没有构造新的类型。
13.4 链表的建立、插入和删除一、单链表第十三章 结构体与共用体
[例 13.7]分配一块区域,输入一个学生数据。
main( )
{ struct stu
{ int num; char *name; char sex; float score;
} *ps;
ps=(struct stu*)malloc(sizeof(struct stu));
ps- >num=102; ps- >name="Zhang ping";
ps- >sex=?M?; ps- >score=62.5;
printf("Number=%d\nName=%s\n",ps- >num,ps
- >name);
printf("Sex=%c\nScore=%f\n",ps->sex,ps->score);
free(ps);
}
13.4 链表的建立、插入和删除一、单链表第十三章 结构体与共用体
单链表有一个头结点 head,指向链表在内存的首地址。
链表中的每一个结点的数据类型为结构体类型,结点有两个成员:整型成员和指向下一个结构体类型结点的指针即下一个结点的地址。
链表按此结构对各结点的访问需从链表的头找起,后续结点的地址由当前结点给出。无论在表中访问那一个结点,都需要从链表的头开始,顺序向后查找。链表的尾结点由于无后续结点,其指针域为空,写作为 NULL。
13.4 链表的建立、插入和删除一、单链表第十三章 结构体与共用体
链表中的各结点在内存的存储地址不是连续的,其各结点的地址是在需要时向系统申请分配的,系统根据内存的当前情况,既可以连续分配地址,也可以跳跃式分配地址。
13.4 链表的建立、插入和删除一、单链表第十三章 结构体与共用体
链表结点的数据结构定义:
struct node
{ int num;
struct node *p;
} ;
在链表结点的定义中,除一个整型的成员外,成员 p是指向与结点 类型完全相同的指针。
13.4 链表的建立、插入和删除一、单链表第十三章 结构体与共用体
1.建立链表是指从无到有地建立起一个链表,即一个一个地输入各结点数据,并建立起前后相链的关系。
单链表的创建过程有以下几步:
1 ) 定义链表的数据结构。
2 ) 创建一个空表。
3 ) 利用 malloc( )函数向系统申请分配一个结点。
4 ) 将新结点的指针成员赋值为空。若是空表,将新结点连接到表头;若是非空表,将新结点接到表尾。
5 ) 判断一下是否有后续结点要接入链表,若有转到 3 ),
否则结束。
13.4 链表的建立、插入和删除二、链表的建立、输出、插入和删除第十三章 结构体与共用体
[例 13.8]写一函数建立一个有若干学生数据的单向链表。
算法:
(1)头指针 head开辟一个空间,并使指针 p和 q指向 head所开辟的空间;
(2)输入学号和成绩;
(3)如果学号不等于 0,p去开辟新的空间,否则程序流程转到步骤 (7);
(4)将数据存入新结点的成员变量中;
(5)根据指针 q和 p连接新旧两个结点后,使 q指向 p所开辟的空间;
(6)输入学号和成绩,并转到步骤 (3)
(7)给 p的 next成员赋 NULL。
13.4 链表的建立、插入和删除二、链表的建立、输出、插入和删除第十三章 结构体与共用体
# indclude <stdio.h>
typedef struct student
{ long num; float score; struct student * next;
} LIST;
LIST * creat ( )
{ long n; float s; LIST * head,*p,*q;
head=(LIST ) malloc(sizeof ( LIST ) );
while ( n!=0)
{ p=((LIST *)malloc(sizeof (LIST)); p->num =; p->score =s;
q-> next = p; q=p ; scanf ("%ld,%f",&n,&s);
q-> next = NULL; return ( head );
}
void print ( LIST * head )
{ } * 函数体是空的,在例 13.7给出其函数 */
main ( )
{ LIST * head; head= creat ( ) ;
print ( head );
}
13.4 链表的建立、插入和删除二、链表的建立、输出、插入和删除第十三章 结构体与共用体
2,输出链表单链表的输出过程有以下几步
(1) 找到表头。
(2) 若是非空表,输出结点的值成员,是空表则退出。
(3) 跟踪链表的增长,即找到下一个结点的地址。
(4) 转到 (2 )。
13.4 链表的建立、插入和删除二、链表的建立、输出、插入和删除第十三章 结构体与共用体
[例 13.9]将链表中各结点的数据依此输出。
其算法是:根据头指针 head,使 p指向第一个结点,输出 p
所指的结点,然后使 p后移一个结点,再输出,直到链表的尾结点。
void print ( LIST * head ) / *head 指向头结点 */
{ LIST * p ;
p= head ->next; / *p指向第一个结点 */
while ( p! =NULL)
{ printf ("%ld %5.1f\n",p->num,p->score);
p=p->next; / *指向下一个结点 */
}
}
13.4 链表的建立、插入和删除二、链表的建立、输出、插入和删除第十三章 结构体与共用体
3,对链表的插入
[例 13.10] 设链表中各结点中的成员项 num(学号)是按学号由小到大顺序排列的。将一个结点插入到链表中,使插入后的链表仍保持原来的规律。
步骤如下:
(1)查找插入位置
(2)插入新结点,即新结点与插入点到前后的结点连。
13.4 链表的建立、插入和删除二、链表的建立、输出、插入和删除第十三章 结构体与共用体
# include< stdio.h>
typedef struct student
{ …… } LIST ;
LIST * creat ( )
{ …… }
void print (LIST * head )
{ …… }
insert (LIST *head,LIST *stud )
{ LIST * p,*q;
q=head; p=head-> next;
while ( p! =NULL && stud -> num > p-> num)
{ q =p; p= p-> next; /*查找插入位置 */
}
stud-> next =p; q-> next =stud ;
}
main ( )
{ LIST * head,stu ; head=creat ( ); print (head );
scanf ("%1d,%f",&stu.num,&stu,score);
insert (head,&stu); print (head );
}
13.4 链表的建立、插入和删除二、链表的建立、输出、插入和删除第十三章 结构体与共用体
4.对链表的删除
[例 13.11]写一函数以删除指定的结点。假设学号作为删除结点的标志。
这相当于幼儿园一队小孩中有一人想离队解手,
经老帅同意后他的从手与前后两个小孩脱钩,
离开队列,而原来在他前面小孩的手去拉原来在他后面小孩的手,这就是删除“结点的“的操作。
步骤如下:
(1)查找删除结点的位置;
(2)删除结点,即将该结点的前一个结点与后一个结点连接。
13.4 链表的建立、插入和删除二、链表的建立、输出、插入和删除第十三章 结构体与共用体
# include< stdio.h>
typedef struct student
{ …,} LIST ;
LIST * creat ( )
{ ….,}
void print (LIST *head )
{ ….,}
int del ( LIST * head,long num )
{ LIST *p,*q;
if(head -> next = = NULL)
{ printf (“List is null! \n”); return 0; }
q= head; p= head -> neat ;
while ( p! = NULL && num! =p-> num )
{ q = p; p=p-> next ; }
if(p = = NULL)
{ printf (" Not heen found,/n" ); reurn 0 ; }
else
{ q-> next=-> next ; free ( p ); return 1; }
}
13.4 链表的建立、插入和删除二、链表的建立、输出、插入和删除第十三章 结构体与共用体
main ( )
{ int a ;
long num;
LIST *head;
head= creat ( );
print (head );
scanf (" %1d",&num );
a= del (head,num );
if (a! =0 ) print (head ); /*如果删除成功,输出链表 */
}
13.4 链表的建立、插入和删除二、链表的建立、输出、插入和删除第十三章 结构体与共用体
“共用体”类型变量的定义形式为:
union 共用体名
{ 成员表列
} 变量表列;
如,union data
{ int i ;
char ch;
float f;
} a,b,c ;
13.5 共用体一、共用体的概念第十三章 结构体与共用体也可以将类型定义与变量定义分开:
union data
{ int i;
char ch;
floar f;
} ;
union data a,b,c;
也可以直接定义共用体变量:
union
{ int i;
char ch;
float f;
} a,b,c;
13.5 共用体一、共用体的概念第十三章 结构体与共用体
只有先定义用体变量才能引用它,而且不能直接引用共用体变量,而只能引用共用体变量中的成员。
如:前面定义了 a,b,c为共用体变量,
下面的引用方式是正确的:
a.i (引用共用体变量的整型变量 i)
a.ch(引用共用体变量中的字符变量 ch)
a.f (引用共用体变量中的实型变量 f)
13.5 共用体二、共用体变量的引用方式第十三章 结构体与共用体
在使用共用体类型数据时要注意以下特点:
(1)同一个内存段可以用来放几个不同类型的成员,但在每一瞬时只能存放其中一种,而不是同时存放几种。
即,每一瞬时只有一个成员起作用,其它的成员不起作用,即不是同时都存在和起作用。
(2)共用体变量中起作用的成员是最后一次存放的成员,
在存入一个新的成员后原有的成员就失去作用。
如有以下赋值语句:
a.i = l; a.c =' a '; a.f = l.5
在完成以上三个赋值运算以后,只有 a.f是有效的,a.i
和 a.c 已经不是原来的值。
13.5 共用体三、共用体类型数据的特点第十三章 结构体与共用体
(3) 共用体变量的地址和它的各成员的地址都是同一地址。
如:& a,&a.i,&a.c,&a.f都是同一地址值。
(4) 不能对共用体变量名赋值,也不能企图引用变量名来得到成员的值,又不能在定义共用体变量时对它初始化。
如:下面这都是不对的:
① union
{ int i;
char ch;
float f;
} a= { 1,' a ',1.5 } ; (不能初始化 )
② a=1; (不能对共用体变量赋值 )
③ m=a; (不能引用共用体变量名以得到值 )
13.5 共用体三、共用体类型数据的特点第十三章 结构体与共用体
(5)不能把共用体变量作为函数参数,也不能使函数带回共用变量,但可以使用指向共用体变量的指针(与结构体变量这种用法相仿)。
(6)共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。反之,结构体也可以出现在共用体类型定义中,
数组也可以作为共用体的成员。
13.5 共用体三、共用体类型数据的特点