8
结构体 &共用体
2
例 1:输出下表学号 姓名 性别 数学 英 语 计算机 C语言 通信网 平均分
101 张三 M 80 90 98 86 90 88.8
102 李四 M 82 89 94 81 91 87.4
103 王五 F 92 78 86 79 89 84.8
104 何六 M 89 81 78 63 76 77.4
105 周七 F 98 69 91 85 67 82
106 … …
用什么样的数据类型存储?缺点?分配内存不集中,寻址效率不高
对数组进行赋初值时,容易发生错位
结构显得比较零散,不容易管理按行处理如何定义数据类型呢?
? stu [30];
3
结构体类型
C语言没有为我们提供相应的数据类型,
但我们可以根据需要自己定义数据类型。
根据题意,我们定义如下的结构体类型:
struct student
{int num;
char name[20];
char sex;
float score[5];
float aver;
};
结构类型的 成员
!!!注意:
struct student
是一个 数据类型
4
结构体变量的定义
struct student stu[30];
含义:定义一个一维数组,有 30个元素,其数据类型为 struct student
为了方便后边的使用,我们可以用 typedef
为这个数据类型起个 别名,
typedef struct student STU;
STU 和其它类型一样使用。
下列语句什么含义?
STU a;STU stu1[30];
5
内存分配
共 30个数组元素,每个数组元素分配多少字节?
可用 sizeof(struct student )求得,
101 张三 M 80 90 98 86 90 88.8
102 李四 M 82 89 94 81 91 87.4
103 王五 F 92 78 86 79 89 84.8
104 何六 M 89 81 78 63 76 77.4
105 周七 F 98 69 91 85 67 82
106 … …
stu
stu[0]
stu[1]
stu[2]
stu[3]
stu[4]
……?那每个学生的学号、姓名,… 又如何表示呢?
6
引用 结构体变量的 成员
结构体变量的 成员 (member),也称 元素
( element) 或 域( filed) 。
使用引用运算符,对结构类型变量 stu[i]中每个成员进行引用
例:
stu[i].num
stu[i].name
它们都是变量,与其它同类型变量同样使用。
注意区分:
结构体 类型 ;
结构体 变量 ;
结构体变量的 成员
7
例 1的实现
算法
定义结构体类型
定义结构体变量
输入
输出
8
例 1 ( 1)
#include <stdio.h>
struct student
{int num; /* 学号 */
char name[20]; /* 姓名 */
char sex; /* 性别,m-男,f-女 */
float score[5]; /* 成绩 */
float aver; /* 平均分 */
}; /* 定义结构体类型 struct student,一般都定义为全局可用的数据类型 */
typedef struct student STU;/*为数据类型起别名 */
void main()
{ int i,j;
float sum;
STU stu[30]; /* 定义结构体变量 */
9
例 1 ( 2)
for(i=0;i<30;i++) /* 输入每个学生的信息 */
{printf(“please input the number\n:”);
scanf(“%d”,&stu[i].num);getchar();
printf(“please input the name\n:”);
scanf(“%s”,stu[i].name);getchar();
printf(“f or m(female or mail)?\n:”);
scanf(“%c”,&stu[i].sex);
printf(“please input 5 scores:\n:”);
for(j=0;j<5;j++) /*输入 5门课的成绩 */
scanf(“%f”,&stu[i].score[j]);
sum=0; /*下边计算 5门课的平均分,并存放在相应成员变量中 */
for(j=0;j<5;j++)
sum+=stu[i].score[j];
stu[i].aver=sum/5;
}
printf("num\tname\t\t sex c1 c2 c3 c4 c5 aver\n");
for(i=0;i<30;i++) /* 输出学生基本信息 */
{printf("%d\t%s\t %c %3.0f %3.0f %3.0f %3.0f %3.0f %4.1f\n",
stu[i].num,stu[i].name,stu[i].sex,stu[i].score[1],stu[i].score[2],
stu[i].score[3],stu[i].score[4],stu[i].score[5],stu[i].average);
}
10
输入写成函数
void Input( STU a[],int n)
{
int i,j;
float sum;
for(i=0;i<n;i++) /* 输入每个学生的信息 */
{printf(“please input the number\n:”);
scanf(“%d”,&stu[i].num);getchar();
printf(“please input the name\n:”);
scanf(“%s”,stu[i].name);getchar();
printf(“f or m(female or mail)?\n:”);
scanf(“%c”,&stu[i].sex);
printf(“please input 5 scores:\n:”);
for(j=0;j<5;j++) /*输入 5门课的成绩 */
scanf(“%f”,&stu[i].score[j]);
sum=0; /*下边计算 5门课的平均分,并存放在相应成员变量中
*/
for(j=0;j<NUMOFCOURSE;j++)
sum+=stu[i].score[j];
stu[i].aver=sum/ NUMOFCOURSE;
}
}
11
主函数
#include <stdio.h>
#define NUMOFCOURSE 5
#define N 30
struct student
{int num; /* 学号 */
char name[20]; /* 姓名 */
char sex; /* 性别,m-男,f-女 */
float score[NUMOFCOURSE]; /* 成绩 */
float aver; /* 平均分 */
}; /* 定义结构体类型 struct student,一般都定义为全局可用的数据类型 */
typedef struct student STU;/*为数据类型起别名 */
void Input( STU a[],int n);
void main()
{ int i,j;
STU stu[N]; /* 定义结构体变量 */
Input(stu,N);
printf("num\tname\t sex c1 c2 c3 c4 c5 aver\n");
for(i=0;i<N;i++) /* 输出学生基本信息 */
{ printf("%d\t%s\t %c,,stu[i].num,stu[i].name,stu[i].sex);
for(j=0;j< NUMOFCOURSE;j++)
printf("%3.0f ",stu[i].score[j];
printf("%3.0f ",stu[i].average);
}
}
12
对数组元素的成员进行 引用有三种方式
stu[i].num
101 张三 M 80 90 98 86 90 88.8
102 李四 M 82 89 94 81 91 87.4
103 王五 F 92 78 86 79 89 84.8
104 何六 M 89 81 78 63 76 77.4
105 周七 F 98 69 91 85 67 82
106 … …
stu
stu[0]
stu[1]
stu[2]
stu[3]
stu[4]
……
1.如果有指针变量 p指向数组 stu,
如何定义指针,如何用指针引用数组元素的成员呢?
2.? *p;
3.P=?;
4.?num
5.P++以后到哪儿?
struct student *p;
p=stu;
… p->num或 (*p).num stu[i].num
13
用指针:例 1 ( 1)
#include <stdio.h>
struct student
{int num; /* 学号 */
char name[20]; /* 姓名 */
char sex; /* 性别,m-男,f-女 */
float score[5]; /* 成绩 */
float aver; /* 平均分 */
}; /* 定义结构体类型 struct student,一般都定义为全局可用的数据类型 */
typedef struct student STU;/*为数据类型起别名 */
void main()
{ int i,j;
float sum;
STU stu[30]; /* 定义结构体变量 */
STU *p;
14
例 1 ( 2)
for(p=stu;p<stu+30;p++) /* 输入每个学生的信息 */
{printf(“please input the number\n:”);
scanf(“%d”,&p->num);
printf(“please input the name\n:”);
scanf(“%s”,p->name);
printf(“f or m(female or mail)?\n:”);
scanf(“%c”,&p->sex);
printf(“please input 5 scores:\n:”);
for(j=0;j<5;j++) /*输入 5门课的成绩 */
scanf(“%f”,&p->score[j]);
sum=0; /*下边计算 5门课的平均分,并存放在相应成员变量中 */
for(j=0;j<5;j++)
sum+=p->score[j];
p->aver=sum/5;
}
printf("num\tname\t\t sex c1 c2 c3 c4 c5 aver\n");
for(p=stu;p<stu+30;p++) /* 输出学生基本信息 */
{printf("%d\t%s\t %c %3.0f %3.0f %3.0f %3.0f %3.0f %4.1f\n",
(*p).num,(*p).name,(*p).sex,(*p).score[1],(*p).score[2],
(*p).score[3],(*p).score[4],(*p).score[5],(*p).average);
}
15
如何定义数据类型存储下列表格中的数据姓名
name
年龄
age
所在办公室 office工作
job (或班级 class)
struct person
{char name[10];
int age;
char job;
depa;
};
需要一个数据单元,但不同情况下存不同的数据。
李四 21 学生 0600001
张三 30 教师 计算机基础教学部
union DEPT
{ int class;
char office[10];
}
union DEPT
{ int class;
char office[10];
};/*先定义共用体类型 */
struct person
{char name[10];
int age;
char job;
union DEPT depa;
};
共用体类型
union DEPT
16
#include <stdio.h>
struct person
{ char name[10];
int age;
char job;
union DEPT
{ int class;
char office[10];
}depa;
};
void main()
{ struct person ps[4];
int n,i;
for(i=0;i<4;i++)
{scanf("%s %d %c",ps[i].name,&ps[i].age,&ps[i].job);
if(ps[i].job==?s?) /*要先判断 job是什么,然后决定存什么 */
scanf("%d",&ps[i].depa.class);
else if(ps[i].job=='t')
scanf("%s",ps[i].depa.office);
}
例 2
printf("\nName Age Job Class/office\n");
for(i=0;i<4;i++)
{ if(ps[i].job==?s?)/*输出的时候同样先判断,再决定输出什么 */
printf("%-10s%-6d%-3c%-10d\n",
ps[i].name,ps[i].age,ps[i].job,ps[i].depa.class);
else
printf("%-10s%-6d%-3c%-10s\n",
ps[i].name,ps[i].age,ps[i].job,ps[i].depa.office);
}
}
17
共用体类型数据的特点
union example
{ short x;
char ch[4];
}a;
a.ch[0]
a.ch[1]
a.ch[2]
a.ch[3]
a.x0F15
0F16
0F17
0F18
在内存中:
1.sizeof(union xxx)取决于 占空间最多 的那个成员变量
2.a.x和 a.ch处于 同样的地址
3.同一内存单元在每一时刻只能存放一个成员的值;
18
例 3 读取 16位整型数据的高字节数据
#include <stdio.h>
union data
{ short i;
char c[2];
};/*这种类型的变量可以看成一个整型变量,也可以看成两个字符型变量,字符型变量对应的 ASCII码即对应整型数的高,低字节 */
typedef union data DATA;
void main()
{ DATA r,*s=&r;
s->i=0x3833; /*换算成二进制为 00111000 00110011 */
printf("\n");
printf("c[0]=%d,c[1]=%d\n",r.c[0],s->c[1]);
}
00110011
00111000
r.c [0]
r.c[1]
r.i0F15
0F16
19
问题:数组有何缺点?
数组必须占据 连续 内存,在数组元素的插入或删除时,费时费力 。
数组的 长度从定义起就固定不变 。如果数据元素的个数不可预知时,就要将数组定义得足够大以备不时之需,这就会造成空间的浪费;此外,数组一经定义就占据内存,直至程序结束。
20
引入 链表 的原因
最主要的是 插入、删除操作的灵活性
能够根据需要灵活申请和释放内存空间。
缺点?
data nexthead data next data NULLdata next
data next
21
链表
一种 数据结构,用顺序,不连续 的内存空间存储数据。
链表中每个节点的数据类型( Linked table)
struct Link
{ int data;
struct Link *next;
}
data nexthead data next data next data NULL
22
例 4 创建链表并存入数据
算法:
循环执行下列操作:
1、创建新节点
1、申请一个节点所用的内存;
2、向该节点存入数据;
2、将该节点链入链表尾部;
struct student
{ char num[10];
float score;
struct student *next;
};/*每个节点的数据类型 */
23
创建一个新节点
struct student *CreateNode()
{
struct student *p;
p = (struct Link *)malloc(sizeof(struct Link)); /* 动态申请一段内存 */
if(p == NULL) /* 申请失败,打印错误信息,退出程序 */
{ printf("No enough memory to alloc");
exit(0); /*结束整个程序的运行 */
}
/*为新建节点赋值 */
p->next = NULL;/* 新建的节点指针域赋空指针 */
printf(“please input number:”); /* 为新建的节点数据区赋值 */
gets(p->num);
printf(“please input score:”);
scanf(“%d”,&p->score);
printf("\n successful create a new node!");
return p;
}
24
struct student *createList( void )
{ struct student *head=NULL,*pr,p; /*开始时是空链表 */
int count=0; char c;
printf(“开始建立链表,请根据提示输入数据:,);
do/*循环实现建立链表 */
{ printf("\nPlease press 'y' to insert one new node,press 'n' to finish:");
c = getchar();
if ((c!='y'||c!='Y')&&(c!='n'||c!='N'))
{/* 如果键入既不是 ' y ',又不是 'n'则循环继续进行 */
puts("you must input 'y' or 'n'");
continue;
}
if ((c=='n'||c=='N')) /* 如果键入的是 'n'循环退出 */
break;
p= CreateNode();
if (count==0)/* 如果是第一个节点,将新节点链至头节点后 */
{ head=p;
pr = head;//使用 pr跟踪当前节点的前一个节点
}
else/* 不是第一个节点,将新建节点接到链表的结尾 pr处 */
{ pr->next = p;
pr = pr->next;//使用 pr跟踪当前节点的前一个节点
}
count++;
} while(1);
return(head); /*返回链表的头结点 */
}
创建链表
25
输出创建的链表
void PrintList(struct student *head)
{ struct student *p;
p=head;
if(head==NULL) /*判断是否为空链表 */
{ printf("List is empty! \n");
else
{ while(p!=NULL)
{ printf("%5s %4.1f\n ",p->num,p->score);
p=p->next; /*指向下一个节点 */
}
}
}
26
链表中插入节点
Head=p;
p->next=head;
Head=p;
p->next= pr ->next;
pr ->next =p;
p
p
p
pr
p
27
struct student *InsertNode(struct student *head,struct student *p,int i)
{
struct student *pr;
if(head==NULL)
{ head=p; p->next=NULL; }/*插入到空链表 */
else
if(i==0)
{ p->next=head; head=p; } /*插入到表头 */
else /*插入到第 i(!=0)个位置 */
{ pr=head;
for( ;pr!=NULL&&i>1;pr=pr->next,i--); /*找到第 i(!=0)个位置 */
if(pr==NULL)
printf("Out of the range,can?t insert p node!\n");
else
{p->next=pr->next; pr->next=p;}
}
return(head);
}
插入节点
28
链表中删除节点
(1)删除第一个结点
(2)删除中间结点
head=head->next;
pr->next =p->next;
p
pr
29
struct student *DeleteNode(struct student *head,char num[])
{struct student *pr;
struct student *p;
if(head==NULL)
{ printf("\nList is empty\n");
return(head);
}
if(strcmp(head->num,num)==0) /*删除第一个结点 */
{ p=head; head=head->next; free(p); /*不要忘了释放内存 */
}
else /*删除其它位置上满足条件的结点 */
{ pr=head; p=head->next;
while(p!=NULL&& strcmp(p->num,num)!=0)
{ pr=p;
p=p->next;
} /*找满足条件的结点 */
if(p!=NULL) /*找到,删除 */
{ pr->next=p->next; free(p); /*不要忘了释放内存 */
}
else
{ printf("%5s not been found\n",num);}
}
return(head);
}
删除节点
30
链表中的要点
用于建立更加灵活的动态数组,每个数组元素均是一个节点。因此,首先应声明每个节点的数据类型 (结构体类型的声明 )
无论是哪种操作 (建立,插入,删除 ),都是 指针的重新指向 问题,即对指针 (head或 pr,p指向的节点的 next成员 )重新赋值的过程。
31
小结
共用体
基本概念
定义、初始化、引用
链表
概念以及数据类型定义
建立、输出、插入、删除等应用。
下节课讲第七、八章剩余内容及总结
32
提高作业
编写程序,实现 (每一个写成一个函数 ):
创建链表,并将学生的学号、姓名和 N门课成绩写入链表,并计算每个学生的总分和平均分。
增加新节点 (增加一个学生的信息 )
查找并输出某个学生的信息 (写成两个函数,分别按学号和姓名查找 )
删除节点 (删除某个学生的记录,可以按姓名或学号查找,然后再删除 )
修改某个学生的信息
对节点进行排序 (选做 )
显示所有学生信息
释放内存
在主函数中创建一个菜单,由用户选择可进行的操作。
33
小结
结构类型以及结构体变量和数组的处理
共用体类型与结构类型的区别
链表 (自学 )
34
作业 1
1.按照下列表格定义结构体数据类型。编写函数实现:
1.输入 5个职工的数据,前两个字段由键盘输入,个人所得税由计算机自动计算
2.输入一个人的名字,输出该职工的所有数据。
3.按个人所得税从大到小排序。
姓名 工资 个人所得税张三 2090 =(工资 -2000)*0.5