第 8章 结构体、共用体和枚举类型本章要点
8.1 复杂数据类型概述
8.2 结构体类型和结构体变量
8.3 结构体变量的使用和初始化
8.4 结构体数据的输入 /输出
8.5 结构体变量作函数参数
8.6 带有位段成员的结构体
8.7 链表
8.8 共用体类型
8.9 枚举类型
8.10 实例演示:“极限俱乐部会员信息系统”程序
本章要点
◆ 结构体、共用体和枚举类型三种构造类型的定义形式、成员类型
◆ 动态数据结构 ——链表
8.1复杂数据类型概述
在第 2章介绍过数据类型有基本数据类型和构造类型之分,
前几章的程序设计都是围绕基本数据类型 (整型、实型和字符型 )以及由相同数据类型的集合 ——数组而展开的。
本章讨论构造类型 ——结构体类型和共用体 (联合体 )类型。
构造类型的定义比较自由,用户可以定义出形式多样的数据类型。它们有一个共同特点是可以由不同数据类型组成,并且一旦定义以后,就可定义相应类型的变量和数组。
8.2 结构体类型和结构体变量
8.2.1结构体类型的定义
结构型是一种较为复杂但却非常灵活的构造型数据类型,
是数目固定、类型不同的若干有序变量的集合。一个结构体类型由若干个称为成员 (或域 )的成分组成。其中,结构体类型的成员允许为不同的数据类型,在 C程序中使用保留字 struct定义结构体类型。结构体类型定义格式为:
struct结构体类型名
{
类型名 1成员名 1;
类型名 2成员名 2;
……
类型名 n成员名 n;
};
说明:
(1)结构体类型名的命名应该符合 C语言中标识符的命名规则。
(2)结构体类型的成员表用花括弧括起来,结构体类型定义完成时使用分号结束。
(3)结构体类型各成员的定义方法与变量相同,可以是 C语言提供的任何数据类型,成员名的命名规则也与变量相同,
各成员定义之间用分号分隔开。
在结构体类型定义中要注意以下几点:
( 1)结构体类型定义中不允许对结构体本身递归定义
( 2)结构体类型定义中可以包含另外的结构体,即结构体是可以嵌套的。
( 3)结构体类型是设计者根据不同对象来定义的,所以结构体类型并非只有一种,而是根据所描述的对象的不同有很多种,各种结构体类型都可以有自己的结构体名,可以包含不同数目的成员。
( 4)定义了一个结构体类型以后,并不意味着已分配一段内存单元来存放这些结构体成员,此时只是定义了类型而不分配内存单元。只定义数据类型系统不分配内存单元,就像整型 (int)或实型 (float)一样,数据类型不占内存单元。
( 5)结构体中的成员名可以和程序中其他地方的变量名相同,两者互不干扰。如程序中定义了一个整型变量 pay和
struct Worker中的成员 pay代表不同的对象,互不干扰。
( 6)同名结构体类型不可以重复定义
8.2.2 结构体变量的定义
先定义结构体类型,再定义结构体类型的变量
在定义结构体类型的同时定义结构体类型的变量
直接定义结构体类型的变量
8.3 结构体变量的使用和初始化
结构体变量的使用一般可分为两种情况:一种是结构体成员的使用;另一种是结构体变量整体的使用。
8.3.1结构体变量成员的引用
引用的一般形式为:
结构体变量名,成员名
,,” 是成员运算符,它在所有运算符中优先级最高。
【 例 8-1】 建立一个简单的学生信息表。
#include<stdio.h>
main()
{ struct student
{ char name[20];
char sex;
int age;
float score; }stu;
printf("input name:");
gets(stu.name);
printf("input sex,");
stu.sex=getchar();
printf("input age:");
scanf("%d",&stu.age);
printf("input score,");
scanf("%f",&stu.score);
printf("name,%s,sex,%c,age,%d,score:
%5.2f\n",stu.name,stu.sex,stu.age,stu.score);}
运行结果为:
8.3.2 结构体变量的初始化结构体变量初始化就是在定义结构体变量的同时给变量提供初值,就一般的变量而言实际上是给变量的成员赋初值。
1)给结构体指针变量赋初值
2)给结构体变量赋初值
3)给结构体数组赋初值结构体变量初始化,并输出其初始值 。
#include<stdio.h>
struct student
{ int num;
char name[15];
char sex;
int age;
float score;
}st1={20001,"WangHong",'F',20,95.5};
main()
{ printf("student st1\n");
printf("%d,%s,%c,%d,%5.1f\n",st1.num,st1.name,s
t1.sex,st1.age,st1.score);}
运行结果:
8.3.3结构体变量的使用
注意:结构体变量只允许整体赋值,其他操作如赋值、输入、输出、运算等 必须通过引用结构体变量的成员进行相应的操作。
【 例 8-4】 结构体变量 (整体 )使用示例。
#include<stdio.h>
struct Student
{ char name[20];
float score;};
main()
{ struct Student wa={"ppp",98.0},wb={"WWW",68.0};
struct Student *p,w[3]={"kkk",88.0};
w[1]=wa; /*结构体变量 (整体 )赋值 */
w[2]=wb; /*结构体变量 (整体 )赋值 */
printf("w[0],%s,%f\n",w[0].name,w[0].score);
p=&w[1];
printf("w[1],%s,%f\n",(*p).name,(*p).score);
p=&w[2];
printf("w[2],%s,%f\n",p->name,p->score);}
运行结果:
8.4 结构体数据的输入 /输出
输入语句中含字符型变量时,在输入格式说明中 %c前要空一格,以便字符型变量能得到正确的输入字符。
程序的输出部分,结构体数组指针变量 p先用数组名
per赋初值,其值跟数组的第 1个结构体元素的首地址
(&per[0])相同。当指针 p自加 1时,那么指针 p就指向第 2个元素的首地址 &per[l],再加 1又指向下一个元素等。
结构体指针只能用来指向结构体数组元素,而不能用来指向结构体数组元素内的成员。结构指针变量也必须要先赋值后才能使用。赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量 。
8.5 结构体变量作函数参数
用结构体的单个成员作为函数参数
用结构体整体作为函数参数
用结构体指针作为函数参数
【 例 8-6】 指向结构体变量的指针作函数参数示例
#include<stdio.h>
struct person
{ char name[20];
char sex;
int age;
float height;};
void output(struct person*p)
{ printf("%-18s%3c%4d%7.1f",p->name,p->sex,p->age,p-
>height);
printf("\n");}
void main()
{ struct person *ptr,per[3]={{"Li Ping",'M',20,175},
{"Wang Ling",'F',19,162.5},
{"Zhao Hui",'M',20,178}};
ptr=per;
for(;ptr<per+3;ptr++) output(ptr);}
程序运行结果为:
8.6 动态数据结构 —— 链表
链表的基本概念
链表由称为结点的元素组成。每个结点都应包含两部分内容:
一是数据部分;二是指针部分。
8.6.2 链表的常用操作链表的建立
定义单链表的数据结构 (亦即定义一个自引用结构 ):
建立表头 (亦即建立一个空表 );
利用 malloc函数向系统申请分配一个结点空间 ;
将新结点的指针成员的值赋为空 (NULL),若是空表,
将新结点连接到表头 ;若非空表,则将新结点连接到表尾 ;
若有后续结点要接入链表,则转到③,否则结束。
8.7 带有位段成员的结构体
计算机应用于过程控制、参数检测和数据通信等领域时,
要求应用程序具有对外部设备进行控制和管理的功能。
它们经常使用的控制方式是向端口发送方式字或命令字,
以及从端口读取状态字等。与端口有关的命令字、方式字和状态字是由以二进制位为单位的字段组成的数据,
它们称为位域数据。 例如,磁带设备的通道信息编码如图所示,
【 例 8-9】 位段结构体应用实例
#include<stdio.h>
struct bitf { unsigned bit_0:1;
unsigned dummy:14;
unsigned bit_15:1;
}*bit_buff;
main()
{ unsigned short x;char buf[20];
printf("这是一个位域结构体应用的例子 \n");
for(;;)
{ printf("请输入一个整型数字 (若想退出请输入 0) >");
gets(buf);
if(buf[0]=='0') break;
x=(unsigned short)atoi(buf);
bit_buff=(struct bitf*)&x;
printf(" >%0x\n",x);
printf("该数在本机内表示的最高位是 MBS >%d",bit_buff->bit_15);
printf("该数在本机内表示的最低位是 LBS >%d",bit_buff->bit_0);
printf("\n");}}
程序运行结果为:
8.8 共用体类型共用体类型的定义
共用体是由类型不相同的若干成员组成,定义的一般形式如下:
union共用体类型名
{
成员表
};
其中,union是保留字,定义方式与结构很相似,它与结构体类型的根本区别是成员表的所有成员在内存中从同一地址开始存放。
【 例 8-10】 结构型和共用型的区别
#include
union data1
{ int i;
float f;
char ch;
};
struct data2
{ int i;
float f;
char ch;
};
main()
{ printf("%d,%d ",sizeof(union data1),sizeof(struct data2));
}
运行结果为:
8.8.2 共用体变量的定义
先定义共用体类型,再定义共用体类型的变量
在定义共用体类型的同时定义共用体变量
直接定义共用体类型的变量
8.8.3 共用体变量的引用
如果对一个共用体变量的不同成员分别赋予不同的值,则只有最后一个被赋值的成员起作用,它的值及其属性就完全代表了当前该共用体变量的值及属性。
与结构体变量相同,不能直接用共用体变量名进行输入输出操作。
C语言允许在两个类型相同的共用体变量之间进行赋值运算。
共用体变量也可以进行初始化,但只是对其第一个成员进行初始化,不能对其所有成员都赋予初值。
【 例 8-11】 将一个整数按字节输出其内容
#include<stdio.h>
union ic
{ int i;
char c[2];
};
main()
{ void i_c(union ic*q);
union ic a,*p;
scanf("%d",&a.i);
p=&a;
i_c(p);
}
void i_c(union ic*q)
{ printf("C0=%o,C1=%o\n",q->c[0],q->c[1]);
printf("C0=%c,C1=%c\n",q->c[0],q->c[1]);
}
运行结果:
8.9 枚举类型枚举型变量的定义,可采用如下 3种方式:
enum color{red,blue,green,yellow,black,
white};
enum color color1,color2;
enum color{red,blue,grin,yellow,black,
white}color1,color2;
enum{red,blue,green,yellow,black,
white}color1,color2;
【 例 8-12】 阅读下面的程序,了解枚举变量的输出方式。
#include<stdio.h>
enum months{JAN=1,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC};
main()
{ enum months month;
char*monthName[]={"","January","February","March","April
",
"May","June","July","Auguest","September",
"October","November","December"};
for(month=JAN;month<=DEC;month++)
printf("%2d %10s\n",month,monthName[month]);
}
程序运行结果:
8,10实例演示:“极限俱乐部会员信息系统”程序
(二)
/******************极限俱乐部会员信息系统 *******************************/
#include "stdio.h" /*控制台 I/O的头文件 */
#include "string.h" /*字符串函数的头文件 */
#include "stdlib.h" /*exit()函数的头文件 */
#define Max 100 /*定义最多可以输入的职工数 */
int Interface(char (*x)[20],int n); /*操作界面 */
int Add(int start,struct staff *p); /*增加职工数 */
void Display( struct staff *p,int start,int end); /*显示已存在的数据 */
void Sort(struct staff *p,int n); /*信息排序 */
int Search (struct staff *p,char *t,int n); /*信息检索 */
int Find(struct staff *p,int n); /*信息查找 */
void Modify(struct staff *p,int n); /*信息修改 */
int Delete(struct staff *p,int n); /*信息删除 */
void Swap(struct staff *p1,struct staff *p2); /*会员信息互换 */
char password[8]="7654321"; /*设置系统管理员密码 */
程序接上页
struct staff
{
char unit[60];
char name[20];
char sex[20];
int age;
int height;
int weight;
};
/*说明:,极限俱乐部会员信息系统,基本信息包括单位、
姓名、性别、年龄、身高、体重六个基本信息,可以根据实际情况有所增减 */
程序接上页
/**********************************************************\
主函数
\**********************************************************/
void main()
{
struct staff sta[Max]; /*结构数组,即可输入的最大会员数,*/
/*最大值为声明中预定义的 Max*/
int s; /*设置变量,以用于用户输入信息的接收,并将其作为转向函数的参数 */
int n=0; /*整型变量 */
char menu[][20]={"添加 ","显示 ","排序 ","检索 ","修改 ","删除 "};
/**********************系统密码管理 ********************/
char choice;
char pass[8];
int flag=0;
int w=3; /*设置输入密码最多允许连续出错的次数 */
do
{
printf("请输入管理员密码,\n");
scanf("%s",pass);
if(!strcmp(pass,password)) /*验证密码 */
程序接上页
{
printf("PASS\n\n\n"); /*密码正确,则输出
"pass"*/
flag=1;
break;
}
else
{
printf("密码错误,请重新输入,\n");
w--;
}
}
while(w>0);
if(!flag)
{
printf("你已连续三次输入错误,请确认后再使用本系统,谢谢 !\n");
exit(0);
}
system("cls"); /*用 system调用 dos命令,清屏 */
程序接上页
/**********************欢迎界面 ********************/
printf("~~~~~~~~~~~~~\t\t\t\t\t~~~~~~~~~~~~~\n");
printf("~~~~~~~~~~~~~\t\t\t\t\t~~~~~~~~~~~~~\n");
printf("\t\t欢迎进入极限俱乐部会员信息系统 \n"); /*标题 */
printf("%%%%%%%%%%%%%%%%%%%%%%%%%%%\t\t\t\t\t%%%%%%%
%%%%%%%%%%%%%%%%%%%%\n");
printf("\n\n\n\n");
printf("单位:北京极限俱乐部 \n"); /*用户单位 */
printf("地址:北京市西城区西四 \n");
printf("会员热线,010-66886688");
printf("\n\n\n\n");
printf("==============\t\t==============\n");
printf("本系统由“天地人和有限公司”开发 \n"); /*开发商 */
printf("地址:北京市海淀区学院路 \n");
printf("作者:天天 \n");
printf("==============\t\t==============\n");
printf("\n\n");
printf("\t\t请任意输入一个值并回车进入系统 \n");
scanf("%s",&choice);
if(choice=='n'||choice=='N')
exit(1);
程序接上页
/**************************欢迎界面结束 *****************************/
system("cls"); /*用 system调用 dos命令,清屏 */
do
{
s=Interface(menu,6);
switch (s) /*转向语句,选择以实现不同的功能 */
{
case 0,n=Add(n,sta);break;
case 1,Display(sta,0,n-1);break;
case 2,Sort(sta,n);break;
case 3,Find(sta,n);break;
case 4,Modify(sta,n);break;
case 5,n=Delete(sta,n);break;
}
}
while(s>=0&&s<=5);
}
程序接上页
/**********************************************************\
操作界面函数
\**********************************************************/
int Interface(char (*x)[20],int n)
{
int i;
int j;
printf("\n××××××× 极限俱乐部会员信息系统 ××××××× \n");
do
{
for (j=0;j<n;j++)
printf("%2d.%s\n",j+1,x[j]);
printf("%2d.返回 \n",j+1);
printf("请从上述选项中选择,\n");
scanf("%d",&i); /*输入选项 */
}
while (i<0||i>n+1);
return (i-1);
}
程序接上页
/**********************************************************\
添加信息函数
\**********************************************************/
int Add(int start,struct staff *p)
{
int n;
for (n=start;n<Max;n++)
{
printf("\n [%d]请输入会员信息 (按 e键退出 ),\n",n+1);
printf(“单位,”);
scanf("%s",(p+n)->unit);
if (strcmp((p+n)->unit,"e")==0) break; /*若输入 e则结束数据输入 */
printf("姓名,");
scanf("%s",(p+n)->name);
printf("性别 (Male/Female):");
scanf("%s",(p+n)->sex);
printf("年龄,");
scanf("%d",&((p+n)->age));
printf("身高 (cm):");
scanf("%d",&((p+n)->height));
printf("体重 (kg):");
scanf("%d",&((p+n)->weight));
}
return n;
}
程序接上页
/**********************************************************\
显示信息函数
\**********************************************************/
void Display(struct staff *p,int start,int end)
{
int i;
printf("\n%10s%10s%10s","单位 ","姓名 ","性别 "); /*显示标题 */
printf("%10s%10s%10s","年龄 ","身高 ","体重 ");
printf("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\n");
for(i=start;i<=end;i++) /*显示数据信息 */
{
printf("%10s%10s%10s",(p+i)->unit,(p+i)->name,(p+i)->sex);
printf("%10d%10d%10d",(p+i)->age,(p+i)->height,(p+i)->weight);
printf("\n");
}
}
程序接上页
/**********************************************************\
按姓名排序函数
\**********************************************************/
void Sort(struct staff *p,int n)
{
int i,j,k;
for (i=0;i<n-1;i++)
{
k=i;
for (j=i+1;j<n;j++)
if(strcmp((p+k)->name,(p+j)->name)>0) k=j;
if(i!=k) Swap(p+k,p+i); /*交换会员信息
*/
}
printf("\n 完成排序 !\n");
}
程序接上页
/**********************************************************\
查找信息函数
\**********************************************************/
int Find(struct staff *p,int n)
{
int i;
char str[20];
printf("\n 请输入会员姓名,");
scanf("%s",str);
i=Search(p,str,n);
if(i==-1)
printf("没有匹配信息,请重试 %s!\n",str);
else
Display(p,i,i);
printf("按任意键回到主界面,\n");
getchar();
return i;
}
int Search(struct staff *p,char *t,int n)
{
int i;
for (i=0;i<n;i++)
if(!strcmp((p+i)->name,t)) /*将输入信息与已有信息比较 */
return i;
return -1;
}
程序接上页
/**********************************************************\
修改信息函数
\**********************************************************/
void Modify(struct staff *p,int n)
{
int i;
i=Find(p,n); /*查找待修改的信息 */
if(i>=0)
{
printf("请输入新信息,\n");
printf("单位,");
scanf("%s",(p+i)->unit);
printf("姓名,");
scanf("%s",(p+i)->name);
printf("性别 (Male/Female):");
scanf("%s",(p+i)->sex);
printf("年龄,");
scanf("%d",&((p+i)->age));
printf("身高 (cm):");
scanf("%d",&((p+i)->height));
printf("体重 (kg):");
scanf("%d",&((p+i)->weight));
printf("\n修改已完成 !\n");
}
}
程序接上页
/**********************************************************\
删除信息函数
\**********************************************************/
int Delete(struct staff *p,int n)
{
int i;
i=Find(p,n); /*查找待删除的信息 */
if(i>=0)
{
p[i]=p[n-1]; /*将最后一条记录覆盖当前记录 */
n=n-1;
printf("\n信息已删除 !\n");
}
return n;
}
程序接上页
/**********************************************************\
信息互换函数函数
\**********************************************************/
void Swap(struct staff *p1,struct staff *p2)
{
struct staff temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}
运行结果运行结果运行结果运行结果运行结果运行结果本章小结
本章详细介绍了结构体、共用体和枚举类型三种构造数据类型的定义结构及成员变量的定义和初始化等问题,
并通过具体的实例详细地说明了其使用方法。建议读者在熟练掌握概念的基础上,对示例程序进行仔细推敲。