【C语言】第9章 用户自己建立数据类型
第9章 用户自己建立数据类型
建立自己的数据类型
声明一个结构体类型的一般形式为:
struct 结构体名
{ 类型名 成员名;};
说明:
(1)结构体类型并非只有一种,而是可以设计出许多种结构体类型
例如
struct Teacher
struct Worker
struct Date等结构体类型
各自包含不同的成员
(2) 成员可以属于另一个结构体类型。
struct Date
{ int month; int day; int year; };
struct Stu
{ int num; char name[20];
char sex; int age;
struct Date birthday;
char addr[30];
};
定义结构体类型变量
-
先声明结构体类型,再定义该类型变量
声明结构体类型struct Student,可以用它来定义变量
struct Student student1,student2; -
在声明类型的同时定义变量
struct Student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
} student1,student2; -
不指定类型名而直接定义结构体类型变量
其一般形式为:
struct
{ 成员表列 }变量名表列;
指定了一个无名的结构体类型 。
例9.1 把一个学生的信息(包括学号、姓名、性别、住址)放在一个结构体变量中,然后输出这个学生的信息。
思路:自己建立一个结构体类型,包括有关学生信息的各成员
用它定义结构体变量,同时赋以初值
输出该结构体变量的各成员
#include <stdio.h>
int main()
{ struct Student
{ long int num; char name[20];
char sex; char addr[20];
}a={10101, “Li Lin”, ‘M’, “123 Beijing Road”};
printf("NO.:%ld\nname:%s\n
sex:%c\naddress:%s\n",
a.num,a.name,a.sex,a.addr);
return 0;
}
例9.2 输入两个学生的学号、姓名和成绩,输出成绩较高学生的学号、姓名和成绩
定义两个结构相同的结构体变量student1和student2;
分别输入两个学生的学号、姓名和成绩;
比较两个学生的成绩,如果学生1的成绩高于学生2,就输出学生1的全部信息,如果学生2的成绩高于学生1,就输出学生2的全部信息。如果二者相等,输出2个学生的全部信息
#include <stdio.h>
int main()
{ struct Student
{ int num;
char name[20];
float score;
}student1,student2;
scanf("%d%s%f",&student1.num, student1.name, &student1.score);
scanf(“%d%s%f”,&student2.num, student2.name, &student2.score);
printf("The higher score is:\n");
if (student1.score>student2.score)
printf("%d %s %6.2f\n",student1.num, student1.name, student1.score);
else if (student1.score<student2.score)
printf("%d %s %6.2f\n",student2.num, student2.name, student2.score);
else
{ printf("%d %s %6.2f\n",student1.num, student1.name, student1.score);
printf("%d %s %6.2f\n",student2.num, student2.name, student2.score);
}
return 0;
}
定义结构体数组
有3个候选人,每个选民只能投票选一人,要求编一个统计选票的程序,先后输入被选人的名字,最后输出各人得票结果。
思路:设一个结构体数组,数组中包含3个元素
每个元素中的信息应包括候选人的姓名(字符型)和得票数(整型)
输入被选人的姓名,然后与数组元素中的“姓名”成员比较,如果相同,就给这个元素中的“得票数”成员的值加1
输出所有元素的信息
#include <string.h>
#include <stdio.h>
struct Person
{ char name[20];
int count;
}leader[3]={ “Li”,0, “Zhang”,0, “Sun”,0 };
int main()
{ int i,j; char leader_name[20];
for (i=1;i<=10;i++)
{ scanf(“%s”,leader_name);
for(j=0;j<3;j++)
if (strcmp(leader_name, leader[j].name)==0)
leader[j].count++;
}
for(i=0;i<3;i++)
printf("%5s:%d\n“,leader[i].name, leader[i].count);
return 0;
}
说明:
(1) 定义结构体数组一般形式是
① struct 结构体名
{成员表列} 数组名[数组长度];
②先声明一个结构体类型,然后再用此类型定义结构体数组:
例如:
struct Person leader[3];
(2) 对结构体数组初始化的形式是在定义数组的后面加上:
={初值表列};
例如:
struct Person leader[3]= {"Li",0,"Zhang",0,"Fun",0};
例9.4 有n个学生的信息(包括学号、姓名、成绩),要求按照成绩的高低顺序输出各学生的信息。
思路:用结构体数组存放n个学生信息,采用选择法对各元素进行排序(进行比较的是各元素中的成绩)。
#include <stdio.h>
struct Student
{ int num; char name[20]; float score; };
int main(){
struct Student stu[5]={
{10101,"Zhang",78 },
{10103,"Wang",98.5},
{10106,"Li", 86},
{10108,“Ling”,73.5},
{10110,“Fun”, 100}};
struct Student temp;
const int n = 5;
int i,j,k;
printf("The order is:\n");
for(i=0;i<n-1;i++)
{ k=i;
for(j=i+1;j<n;j++)
if(stu[j].score>stu[k].score) k=j;
temp=stu[k];
stu[k]=stu[i];
stu[i]=temp;
}
for(i=0;i<n;i++)
printf("%6d %8s %6.2f\n", stu[i].num, stu[i].name, stu[i].score);
printf("\n");
return 0;
}
结构体指针
指向结构体对象的指针变量既可以指向结构体变量,也可以用来指向结构体数组中的元素。
指针变量的基类型必须与结构体变量的类型相同。
例如:
struct Student *pt;
例9.5 通过指向结构体变量的指针变量输出结构体变量中成员的信息。
思路:在已有的基础上,本题要解决两个问题:
怎样对结构体变量成员赋值;
怎样通过指向结构体变量的指针访问结构体变量中成员。
说明:
为了使用方便和直观,C语言允许把(p).num用 p->num来代替
(p).name等价于p->name
如果p指向一个结构体变量stu,以下等价:
① stu.成员名(如stu.num)
② (*p).成员名(如(*p).num)
p->成员名(如p->num)
例9.6 有3个学生的信息,放在结构体数组中,要求输出全部学生的信息。
思路:用指向结构体变量的指针处理
声明struct Student,并定义结构体数组、初始化
定义指向struct Student类型指针p
使p指向数组首元素,输出元素中各信息
使p指向下一个元素,输出元素中各信息
再使p指向结构体数组的下一个元素,输出它指向的元素中的有关信息
#include <stdio.h>
struct Student
{ int num;
char name[20];
char sex;
int age;
};
struct Student stu[3]={
{10101,"Li Lin",'M',18},
{10102,"Zhang Fun",'M',19},
{10104,"Wang Min",'F',20} };
int main()
{ struct Student *p;
printf(" No. Name sex age\n");
for (p=stu;p<stu+3;p++)
printf(“%5d %-20s %2c %4d\n”, p->num, p->name,
p->sex, p->age);
return 0;
}
例9.7 有n个结构体变量,内含学生学号、姓名和3门课程的成绩。要求输出平均成绩最高的学生的信息(包括学号、姓名、3门课程成绩和平均成绩)。
思路:将n个学生的数据表示为结构体数组。按照功能函数化的思想,分别用3个函数来实现不同的功能:
用input函数输入数据和求各学生平均成绩
用max函数找平均成绩最高的学生
用print函数输出成绩最高学生的信息
在主函数中先后调用这3个函数,用指向结构体变量的指针作实参。最后得到结果。
本程序假设n=3
用指针处理链表
例9.8 建立一个如图所示的简单链表,它由3个学生数据的结点组成,要求输出各结点中的数据。
#include <stdio.h>
struct Student
{ int num;
float score;
struct Student *next;
};
int main()
{ struct Student a,b,c,*head,*p;
a. num=10101; a.score=89.5;
b. num=10103; b.score=90;
c. num=10107; c.score=85;
head=&a; a.next=&b;
b.next=&c; c.next=NULL;
p=head;
do
{ printf(“%ld%5.1f\n”,p->num,p->score);
p=p->next;
}while(p!=NULL);
return 0;
}
建立动态链表
所谓建立动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系。
例9.9 写一函数建立一个有3名学生数据的单向动态链表。
定义3个指针变量:head,p1和p2,它们都是用来指向struct Student类型数据
struct Student *head,*p1,*p2;
用malloc函数开辟第一个结点,并使p1和p2指向它
p1=p2=(struct Student*)malloc(LEN);
读入一个学生的数据给p1所指的第一个结点
scanf("%ld,%f",&p1->num,&p1->score);
使head也指向新开辟的结点
再开辟另一个结点并使p1指向它,接着输入该结点的数据
p1=(struct Student*)malloc(LEN);
scanf("%ld,%f",&p1->num,&p1->score);
使第一个结点的next成员指向第二个结点,即连接第一个结点与第二个结点
p2->next=p1;
使p2指向刚才建立的结点
p2=p1;
再开辟另一个结点并使p1指向它,接着输入该结点的数据
p1=(struct Student*)malloc(LEN);
scanf("%ld,%f",&p1->num,&p1->score);
使第二个结点的next成员指向第三个结点,即连接第二个结点与第三个结点
p2->next=p1;
使p2指向刚才建立的结点
p2=p1;
再开辟另一个结点并使p1指向它,接着输入该结点的数据
p1=(struct Student*)malloc(LEN);
scanf("%ld,%f",&p1->num,&p1->score);
输入的学号为0,表示建立链表的过程完成,该结点不应连接到链表中
p2->next=NULL;
#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(struct Student)
struct Student
{ long num;
float score;
struct Student *next;
};
int n;
struct Student *creat(void)
{ struct Student *head,*p1,*p2; n=0;
p1=p2=( struct Student*) malloc(LEN);
scanf(“%ld,%f”,&p1->num,&p1->score);
head=NULL;
while(p1->num!=0)
{ n=n+1;
if(n==1) head=p1;
else p2->next=p1;
p2=p1;
p1=(struct Student*)malloc(LEN); scanf(“%ld,%f”,&p1->num,&p1->score);
}
p2->next=NULL;
return(head);
}
int main()
{ struct Student *pt;
pt=creat();
printf(“\nnum:%ld\nscore:%5.1f\n”, pt->num,pt->score);
return 0;
}
例9.10 编写一个输出链表的函数print。
输出p所指的结点
printf("%ld %5.1f\n",p->num,p->score);
使p后移一个结点
p=p->next;
输出p所指的新结点
printf("%ld %5.1f\n",p->num,p->score);
使p后移一个结点
p=p->next;
void print(struct Student *p)
{
printf("\nThese %d records are:\n",n);
if(p!=NULL)
do
{ printf("%ld %5.1f\n",
p->num,p->score);
p=p->next;
}while(p!=NULL);
}
共用体类型
有时想用同一段内存单元存放不同类型的变量。
使几个不同的变量共享同一段内存的结构,称为 “共用体”类型的结构。
“共用体”与“结构体”的定义形式相似,但它们的含义是不同的。
结构体变量所占内存长度是各成员占的内存长度之和,每个成员分别占有其自己的内存单元。而共用体变量所占的内存长度等于最长的成员的长度。
只有先定义了共用体变量才能引用它,但应注意,不能引用共用体变量,而只能引用共用体变量中的成员。
例如,前面定义了a,b,c为共用体变量,下面的引用方式是正确的:
a.i a.ch a.f
例9.11 有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码、性别、职业、班级。教师的数据包括:姓名、号码、性别、职业、职务。要求用同一个表格来处理。
思路:学生和教师的数据项目多数是相同的,但有一项不同。现要求把它们放在同一表格中
如果job项为s,则第5项为class。即Li是501班的。如果job项是t,则第5项为position。Wang是prof(教授)。
对第5项可以用共用体来处理(将class和position放在同一段存储单元中)
#include <stdio.h>
struct
{ int num;
char name[10];
char sex;
char job;
union
{ int clas;
char position[10];
}category;
}person[2];
#include <stdio.h>
union Categ
{ int clas;
char position[10];
};
struct
{ int num;
char name[10];
char sex;
char job;
union Categ category
}person[2];
int main()
{ int i;
for(i=0;i<2;i++)
{ scanf("%d %s %c %c“,&person[i].num, &person[i].name, &person[i].sex,&person[i].job);
if (person[i].job == 's') scanf("%d“,&person[i].category.clas);
else if(person[i].job == 't‘) scanf(“%s”,person[i].category.position); else printf(“Input error!”);
}
printf("\n");
for (i=0;i<2;i++)
{ if (person[i].job == ‘s’)
printf("%-6d%-10s%-4c%-4c%-10d\n", person[i].num, person[i].name, person[i].sex, person[i].job,
person[i].category.clas);
else
printf("%-6d%-10s%-4c%-4c%-10s\n", person[i].num, person[i]. name, person[i].sex, person[i].job,
person[i].category.position);
}
return 0;
}
使用枚举类型
所谓“枚举”就是指把可能的值一一列举出来,变量的值只限于列举出来的值的范围内
声明枚举类型用enum开头。
例如:
enum Weekday {sun, mon,tue, wed,thu,fri,sat};
声明了一个枚举类型enum Weekday
然后可以用此类型来定义变量
enum Weekday workday, weekend;
workday=mon; 正确
weekend=sun; 正确
Weekday=monday; 不正确
每一个枚举元素都代表一个整数,C语言编译按定义时的顺序默认它们的值为0,1,2,3,4,5…
在上面定义中,sun的值为0,mon的值为1,…sat的值为6
如果有赋值语句: workday=mon; 相当于workday=1;
也可以人为地指定枚举元素的数值,
例如: enum Weekday{sun=7,mon=1,tue,
wed,thu,fri,sat}workday,week_end;
指定枚举常量sun的值为7,mon为1,以后顺序加1,sat为6
枚举元素可以用来作判断比较。例如:
if(workday==mon)…
if(workday>sun)…
枚举元素的比较规则是按其在初始化时指定的整数来进行比较的。
如果定义时未人为指定,则按上面的默认规则处理,即第一个枚举元素的值为0,故mon>sun,sat>fri
例9.12 口袋中有红、黄、蓝、白、黑5种颜色的球若干个。每次从口袋中先后取出3个球,问得到3种不同颜色的球的可能取法,输出每种排列的情况。
#include <stdio.h>
int main()
{ enum Color{red,yellow,blue,white,black};
enum Color i,j,k,pri;
int n, loop;
n=0;
for (i=red;i<=black;i++)
for (j=red;j<=black;j++)
if (i!=j)
{ for (k=red;k<=black;k++)
if ((k!=i) && (k!=j))
{ n=n+1;
printf(“%-4d”,n);
for (loop=1;loop<=3;loop++)
{ switch (loop)
{ case 1: pri=i; break;
case 2: pri=j; break;
case 3: pri=k; break;
default: break;
}
switch (pri)
{ case red: printf(“%-10s”,“red”); break;
case yellow:printf("%-10s","yellow"); break;
case blue: printf(“%-10s”,“blue”); break;
case white: printf(“%-10s”,“white”); break;
case black: printf("%-10s","black"); break;
}
}
printf("\n");
}
}
printf("\ntotal:%5d\n",n);
return 0;
}
用typedef声明新类型名
-
简单地用一个新的类型名代替原有的类型名
typedef int Integer;
typedef float Real;
int i,j; float a,b; 与 Integer i,j; Real a,b; 等价 -
命名一个简单的类型名代替复杂的类型表示方法
(1) 命名一个新的类型名代表结构体类型:
typedef struct
{ int month; int day; int year; } Date;
Date birthday;
Date *p;
(2) 命名一个新的类型名代表数组类型
typedef int Num[100];
Num a;
(3) 命名一个新的类型名代表一个指针类型
typedef char *String;
String p,s[10];
(4) 命名一个新的类型名代表指向函数的指针类型
typedef int (*Pointer)();
Pointer p1,p2;
归纳起来,声明一个新的类型名的方法是
① 先按定义变量的方法写出定义体(int i;)
② 将变量名换成新类型名(将i换成Count)
③ 在最前面加typedef
(typedef int Count)
④ 用新类型名去定义变量
以定义上述的数组类型为例来说明:
① 先按定义数组变量形式书写:int a[100];
② 将变量名a换成自己命名的类型名:int Num[100];
③ 在前面加上typedef,得到typedef int Num[100];
用来定义变量:Num a;
相当于定义了:int a[100];