第 九 章结构体与共用体在实际问题中,一组数据往往具有 不同的数据类型 。
例如,在学生登记表中,姓名应为字符型;学号可为整型或字符型;年龄应为整型;性别应为字符型;
成绩可为整型或实型。因为 一个数组中只能存放同一种数据类型的数据,故不能用一个数组来存放这一组数据;如单独定义为互相独立的简单变量,难以反映它们之间的内在联系。
为更好地解决诸如此类问题,C语言采用 构造数据类型,通过构造数据类型可以很好地将一些不同类型的数据作为一个整体进行处理。
9.1 结构体
9.1.1 结构类型定义和结构变量说明
“结构,是一种构造类型,它是由若干,成员,组成的。每一个成员可以是一个基本数据类型或者又是一个构造类型。结构既是一种,构造,而成的数据类型,
那么在说明和使用之前必须先定义它,也就是构造它。
struct [结构体名 ]
{
类型标识符 成员名;
类型标识符 成员名 ;
…………….
};
成员类型可以是基本型或构造型struct是 关键字,
不能省略合法标识符可省,无名结构体一、结构的定义例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
name
num
sex
age
score
addr
2字节
2字节
20字节
1字节
4字节
30字节

…..
结构体类型定义描述结构的组织形式,不分配内存结构体类型定义的 作用域括号后的分号不可省。结构定义之后,即可进行变量说明。结构是一种复杂的数据类型,是数目固定,类型不同的若干有序变量的集合。
二、结构体变量说明例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
struct student stu1,stu2;
先定义结构体类型,再定义结构体变量
一般形式,struct 结构体名
{
类型标识符 成员名;
类型标识符 成员名;
…………….
};
struct 结构体名 变量名表列 ;例 #define STUDENT struct student
STUDENT
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
STUDENT stu1,stu2;
– 定义结构体类型的同时定义结构体变量一般形式:
struct 结构体名
{
类型标识符 成员名;
类型标识符 成员名;
…………….
}变量名表列 ;
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
– 直接定义结构体变量一般形式:
struct
{
类型标识符 成员名;
类型标识符 成员名;
…………….
}变量名表列 ;
例 struct
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
用 无名结构体 直接定义变量 只能一次
– 说明
结构体类型与结构体变量概念不同
– 类型,不分配内存 ; 变量,分配内存
– 类型,不能赋值、存取、运算 ; 变量,可以
结构体可嵌套
结构体成员名与程序中变量名可相同,不会混淆
结构体类型及变量的作用域与生存期例 struct date
{ int month;
int day;
int year;
};
struct student
{ int num;
char name[20];
struct date birthday;
}stu;
num name birthdaymonth day year
例 struct student
{ int num;
char name[20];
struct date
{ int month;
int day;
int year;
}birthday;
}stu;
num name birthdaymonth day year
9.1.2 结构体变量的引用
– 引用规则
结构体变量 不能整体引用,只能引用变量 成员
可以将一个结 构体变量赋值给另一个结构体变量
结构体嵌套时 逐级引用成员 (分量 )运算符优先级,1
结合性,从左向右引用方式,结构体变量名,成员名例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
stu1.num=10;
stu1.score=85.5;
stu1.score+=stu2.score;
stu1.age++;
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
printf(“%d,%s,%c,%d,%f,%s\n”,stu1); (?)
stu1={101,“Wan Lin”,?M?,19,87.5,“DaLian”}; (?)
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
stu2=stu1; ( )
例 struct student
{ int num;
char name[20];
struct date
{ int month;
int day;
int year;
}birthday;
}stu1,stu2;
num name birthdaymonth day year
stu1.birthday.month=12;
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
if(stu1==stu2)
…….,(?)
一、结构体变量的赋值
【 例 9.1】 给结构变量赋值并输出其值。
main(){
struct stu
{
int num;
char *name;
char sex;
float score;
} student1,student2;
student1.num=102;
student1.name="Zhang ping";
printf("input sex and score\n");
scanf("%c%f",&student1.sex,&student1.score);
student2=student1;
printf("Number=%d\nName=%s\n",student2.num,student2.name);
printf("Sex=%c\nScore=%f\n",student2.sex,student2.score);}
本例演示了结构变量的赋值、输入和输出的方法。
struct 结构体名
{
类型标识符 成员名;
类型标识符 成员名;
…………….
};
struct 结构体名 结构体变量 ={初始数据 };
例 struct student
{ int num;
char name[20];
char sex;
int age;
char addr[30];
};
struct student stu1={112,“Wang Lin”,?M?,19,“200 Beijing Road”};
二、结构体变量的初始化形式一
– 形式二:
struct 结构体名
{
类型标识符 成员名;
类型标识符 成员名;
…………….
}结构体变量 ={初始数据 };
例 struct student
{ int num;
char name[20];
char sex;
int age;
char addr[30];
}stu1={112,“Wang Lin”,?M?,19,“200 Beijing Road”};
– 形式三:
struct
{
类型标识符 成员名;
类型标识符 成员名;
…………….
}结构体变量 ={初始数据 };
例 struct
{ int num;
char name[20];
char sex;
int age;
char addr[30];
}stu1={112,“Wang Lin”,?M?,19,“200 Beijing Road”};
9.1.3 结构体数组
– 结构体数组的定义三种形式:
形式一,
struct student
{ int num;
char name[20];
char sex;
int age;
};
struct student stu[2];
形式二,
struct student
{ int num;
char name[20];
char sex;
int age;
}stu[2];
形式三,
struct
{ int num;
char name[20];
char sex;
int age;
}stu[2];
num
name
sex
age
num
name
sex
age
stu[0]
stu[1]
25B
– 结构体数组初始化例 struct
{ int num;
char name[20];
char sex;
int age;
}stu[ ]={{……},{……},{……}};
顺序初始化,
struct student
{ int num;
char name[20];
char sex;
int age;
};
struct student stu[ ]={100,“Wang Lin”,?M?,20,
101,“Li Gang”,?M?,19,
110,“Liu Yan”,?F?,19};
例 struct student
{ int num;
name[20];
char s x;
int age;
}stu[ ]={{……},{……},{……}};
分行初始化,
struct student
{ int nu ;
char na e[20];
char sex;
int age;
};
struct student stu[ ]={{100,,ang Lin”,,20},
{101,“Li Gang”,,19},
{110,“Liu Yan”,?F?,19}};
全部初始化时维数可省
– 结构体数组引用引用方式,结构体数组名 [下标 ].成员名
stru t student
{ int num;
char name[20];
char sex;
int age;
}str[3];
stu[1].age++;
strcpy(stu[0].name,”ZhaoDa”);
例 统计后选人选票
struct person
{ char name[20];
int count;
}leader[3]={“Li”,0,“Zhang”,0,”Wang“,0};
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);
}
name count
Li
Zhang
Wang
0
0
0
9.1.4 结构体和指针
– 指向结构体变量的指针
定义形式,struct 结构体名 *结构体指针名 ;
例 struct student *p;
使用结构体指针变量引用成员形式 存放结构体变量在内存的起始地址
num
name
sex
age
stu
pstruct student{ int num;
char name[20];
char sex;
int age;
}stu;
struct student *p=&stu;
(*结构体指针名 ).成员名 结构体指针名 ->成员名 结构体变量名,成员名指向运算符优先级,1
结合方向:从左向右例 指向结构体的指针变量
main()
{ struct student
{ long int num;
char name[20];
char sex;
float score;
}stu_1,*p;
p=&stu_1;
stu_1.num=89101;
strcpy(stu_1.name,"Li Lin");
p->sex='M';
p->score=89.5;
printf("\nNo:%ld\nname:%s\nsex:%c\nscore:%f\n",
(*p).num,p->name,stu_1.sex,p->score);
}
例 int n;
int *p=&n;
*p=10;? n=10
struct student stu1;
struct student *p=&stu1;
stu1.num=101;? (*p).num=101
– 指向结构体数组的指针例 指向结构体数组的指针
struct student
{ int num;
char name[20];
char sex;
int age;
}stu[3]={{10101,"Li Lin",'M',18},
{10102,"Zhang Fun",'M',19},
{10104,"Wang Min",'F',20}};
main()
{ struct student *p;
for(p=stu;p<stu+3;p++)
printf("%d%s%c%d\n",p->num,p->name,p->sex,p->age);
}
num
name
sex
age
stu[0]
p
stu[1]
stu[2]
p+1
在上例中,可使用的是 (*p).num这样的形式。 (*p)表示
p指向结构体变量,(*p).num是 p指向的结构体变量中的成员 num。注意,*p两侧的括弧不可省。
请分析以下几种运算:
p->n 得到 p指向的结构体变量中的成员 n的值。
p->n++ 得到 p指向的结构体变量中的成员 n的值,
用完该值后使它加 1。
++p->n 得到 p指向的结构体变量中的成员 n的值使之加 1(先加)。
– 用指向结构体的指针作函数参数
用结构体变量的成员作参数 ----值传递
用指向结构体变量或数组的指针作参数 ----地址传递
用结构体变量作参数 ----多值传递,效率低
struct data
{ int a,b,c; };
main()
{ void func(struct data);
struct data arg;
arg.a=27; arg.b=3; arg.c=arg.a+arg.b;
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
printf("Call Func()....\n");
func(arg);
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
}
void func(struct data parm)
{ printf("parm.a=%d parm.b=%d parm.c=%d\n",parm.a,parm.b,parm.c);
printf("Process...\n");
parm.a=18; parm.b=5; parm.c=parm.a*parm.b;
printf("parm.a=%d parm.b=%d parm.c=%d\n",parm.a,parm.b,parm.c);
printf("Return...\n");
}
arga,27b,3
c,30
(main)
(func)
parm
a,27
b,3
c,30
copy
arg
a,27
b,3
c,30
(main)
(func)
parm
a,18
b,5
c,90
arg
a,27
b,3
c,30
(main)
例 用结构体变量作函数参数
struct data
{ int a,b,c; };
main()
{ void func(struct data *parm);
struct data arg;
arg.a=27; arg.b=3; arg.c=arg.a+arg.b;
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
printf("Call Func()....\n");
func(&arg);
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
}
void func(struct data *parm)
{ printf("parm->a=%d parm->b=%d parm->c=%d\n",parm->a,parm->b,parm->c);
printf("Process...\n");
parm->a=18; parm->b=5; parm->c=parm->a*parm->b;
printf("parm->a=%d parm->b=%d parm->c=%d\n",parm->a,parm->b,parm->c);
printf("Return...\n");
}
arg
a,18
b,5
c,90
(main)
arg
a,27
,3
c,30
ai )例 用结构体指针变量作函数参数
arga,27b,3
c,30
ai )
(func)
parm****
arga,18b,5
c,9
(main)
(f c)
9.1.5 结构体与链表假设我们将结构体类型定义如下:
struct entry
{int value;
struct entry *next;
};
这样就定义了 entry结构体类型,该结构体类型含有两个成员。结构的第一个成员是简单的整数 value。
结构的第二个成员是 next成员,该成员是一个指向
entry结构的指针假设我们把两个变量定义成 struct entry类型如下:
struct entry n1,n2;
执行下列语句可使 n1结构的 next指针指向 n2结构。
n1.next=&n2;
n1.value
n1.next
n2.value
n2.next
假如 n3变量也定义成 struct entry类型,那么用下列语句可加入另一个,链接,。
n2.next=&n3;
n1.value
n1.next
n2.value
n2.next
n3.value
n3.next
【 例 9.7】
#include<stdio.h>
main()
{
struct entry
{
int value;
struct entry *next;
};
struct entry n1,n2,n3;
int i;
n1.value=100;
n2.value=200;
n3.value=300;
n1.next=&n2;
n2.next=&n3;
i=n1.next->value;
printf(“%d”,i);
printf(“%d\n”,n2.next->value);
}
结果输出:
200 300
n1.value
n1.next
n2.value
n2.next
n3.value
n3.next
链表结点的删除语句,n1.next=n2.next; 或 n1.next=&n3;
链表结点的插入
n1.value
n1.next
n2.value
n2.next
n3.value
n3.next
n2_3.value
n2_3.next
语句,n2_3.next=n2.next;n2.next=&n2_3
有关链表操作详细见书本例 9.8,9.9,9.10
9.1.6 C的动态存储分配函数
C语言中不允许动态数组类型。
例如:
int n;
scanf("%d",&n);
int a[n];
这种用变量表示长度,对数组的大小作动态说明,
这是 错误 的。但是在实际的编程中,经常可能出现所需的内存空间取决于实际输入的数据 的情况,事前无法预先确定。对于这种问题,用数组的办法很难解决。为了解决上述问题,C语言提供了一些 内存管理函数,这些内存管理函数可以 按需要动态地分配内存空间,也可 把不再使用的空间回收待用,
为有效地利用内存资源提供了手段。常用的内存管理函数有以下三个:
调用形式:
(类型说明符 *) malloc (size)
功能:在内存的动态存储区中分配一块长度为 "size"
字节的连续区域。函数的返回值为该区域的 首地址 。
,类型说明符,表示把该区域用于何种数据类型。
(类型说明符 *)表示把返回值强制转换为该类型指针。
,size”是一个无符号数。
例如,pc=(char *) malloc (100); 表示分配 100个字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的指针,把该指针赋予指针变量
pc。
1.分配内存空间函数 malloc
calloc 也用于分配内存空间。调用形式:
(类型说明符 *)calloc(n,size)
功能,在内存动态存储区中分配 n块长度为
,size”字节的连续区域。函数的返回值为该区域的首地址。
(类型说明符 *)用于强制类型转换。
2.分配内存空间函数 calloc
calloc函数与 malloc 函数的区别仅在于一次可以分配 n块区域。
例如,ps=(struct stu*) calloc(2,sizeof
(struct stu)); 其中的 sizeof(struct stu)是求
stu的结构长度。因此该语句的意思是:按 stu的长度分配 2块连续区域,强制转换为 stu类型,并把其首地址赋予指针变量 ps。
调用形式:
free(void *ptr);
功能:释放 ptr所指向的一块内存空间,ptr 是一个任意类型的指针变量,它指向被释放区域的首地址。
被释放区应是由 malloc或 calloc函数所分配的区域。
3.释放内存空间函数 free
【 例 9.11】 分配一块区域,输入一个学生数据。
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);}
9.2 联合在实际问题中有很多这样的例子。例如在学校的教师和学生中填写以下表格:姓名、年龄、职业、单位。,职业,一项可分为,教师,和,学生,两类 。
对,单位,一项学生应填入班级编号,教师应填入某系某教研室。 班级可用整型量表示,教研室只能用字符类型。 要求把这两种类型不同的数据都填入
,单位,这个变量中,就必须把,单位,定义为包含整型和字符型数组这两种类型的,联合,。
定义一个联合类型的一般形式为:
union 联合名
{
成员列表;
};
成员表中含有若干成员,成员的一般形式为:类型说明符成员名例如:
union perdata
{
int class;
char office[10];
};
Perdata为联合类型,它含有两个成员,一个为整型,成员名为
class;另一个为字符数组,数组名为 office。联合定义之后,即可进行联合变量说明,被说明为 perdata类型的变量,可以存放整型量 class或存放字符数组 office。
一、联合的定义
– 结构体与共用体
区别,存储方式不同
struct node
{ char ch[2];
int k;
}a;
union node
{ char ch[2];
int k;
}b;
achk
bch k
变量的各成员同时存在任一时刻只有一个成员存在
联系,两者可相互嵌套二、联合变量的说明联合变量的说明和结构变量的说明方式相同,也有三种形式。 即先定义,再说明;定义同时说明和直接说明。
union perdata
{
int class;
char officae[10];
};
union perdata a,b;
先定义,再说明union perdata
{ int class;
char office[10]; }a,b; 同时说明
union
{ int class;
char office[10]; }a,b;
直接说明经说明后的 a,b变量均为 perdata类型。 a,b变量的长度应等于 perdata 的成员中最长的长度,即等于 office数组的长度,共 10个字节。 a,b变量如赋予整型值时,只使用了 2个字节,而赋予字符数组时,可用 10个字节三、联合变量的赋值和使用对联合变量的赋值,使用都只能是对变量的成员进行。
联合变量的成员表示为:联合变量名,成员名
【 例 9.12】 设有一个教师与学生通用的表格,教师数据有姓名,
年龄,职业,教研室四项。学生有姓名,年龄,职业,班级四项。
编程输入人员数据,再以表格输出。
main()
{
struct
{
char name[10];
int age;
char job;
union
{
int class;
char office[10];
} depa;}body[2];
int n,i;
for(i=0;i<2;i++)
{
printf("input name,age,job and
department\n");
scanf("%s %d
%c",body[i].name,&body[i].age,&body[i].
job);
if(body[i].job=='s')
scanf("%d",&body[i].depa.class);
else
scanf("%s",body[i].depa.office);
}
printf("name\tage job class office\n");
for(i=0;i<2;i++)
{
if(body[i].job=='s')
printf("%s\t%3d %3c
%d\n",body[i].name,body[i].age,body[i].
job,body[i].depa.class);
else
printf("%s\t%3d %3c
%s\n",body[i].name,body[i].age,body[i].
job,body[i].depa.office);
}
}
9.3 枚举枚举是一个被命名的整型常数的集合,枚举在日常生活中很常见。例如表示星期的 SUN,MON,TUE,
WED,THU,FRI,SAT 就是一个枚举。
枚举的说明与结构体和联合体相似,其形式为,
enum 枚举名标识符 {
常量标识符 [=整型常数 ],
常量标识符 [=整型常数 ],
..,
常量标识符 [=整型常数 ],
} 枚举变量 ;
如果枚举没有初始化,即省略 "=整型常数 "时,则从第一个标识符开始,顺序给每个标识符一个序列号 0,1,2,...。但当枚举中的某个成员赋值后,
其后的成员则按此数值依次加 1作为其序列号。
例如下列枚举说明后,x1,x2,x3,x4的值分别为
0,1,2,3。
enum string{x1,x2,x3,x4}x;
当定义改变成,
enum string {
x1,
x2=0,
x3=50,
x4,
}x;
则 x1=0,x2=0,x3=50,x4=51
注意,
1,枚举中每个成员 (标识符 )结束符是 ",",不是
";",最后一个成员可省略 ","。
2,初始化时可以赋负数,其后的标识符仍依次加
1。
3,枚举变量只能取枚举说明结构中的某个标识符常量。
例如,
enum string { x1=5,x2,x3,x4,};
enum strig x=x3;
此时,枚举变量 x实际上是 7。
9.4 自定义数据类型
功能,用自定义名字为 已有 数据类型命名
– 类型定义 简单形式,typedef type name;
例 typedef int INTEGER;类型定义语句关键字 已有数据类型名 用户定义的类型名例 typedef float REAL;
类型定义后,与已有类型一样使用例 INTEGER a,b,c;
REAL f1,f2;
int a,b,c;
float f1,f2; 说明,1.typedef 没有创造 新数据类型
2.typedef 是定义类型,不能定义变量
3.typedef 与 define 不同
define typedef
预编译时处理 编译时处理简单字符置换 为已有类型命名
– typedef定义类型步骤
按定义变量方法先写出定义体 如 int i;
将变量名换成新类型名 如 int INTEGER;
最前面加 typedef 如 typedef int INTEGER;
用新类型名定义变量 如 INTEGER i,j;
例 定义数组类型
int a[100];
int ARRAY[100];
typedef int ARRAY[100];
ARRAY a,b,c;
例 定义指针类型
char *str;
char *STRING;
typedef char *STRING;
ST ING p,s[10];
例 定义函数指针类型
int (*p)();
int (*POWER)();
typedef int (*POWER)();
POWER p1,p2;
例 定义结构体类型
struct date
{ int month;
int day;
int year;
}d;
例 定义结构体类型
struct date
{ int month;
int day;
int year;
}DATE;
例 定义结构体类型
typedef struct date
{ int month;
int day;
int year;
}DATE;
例 定义结构体类型
DATE birthday,*p;
– 类型定义可嵌套例 typedef struct club
{ char name[20];
int size;
int year;
}GROUP;
typedef GROUP *PG;
PG pclub;
本章结束 !