第 11章 结构体与共用体结构体共用体用 typedef定义类型枚举类型第 11章结束
请同学们注意结构体与共用体的区别
同时注意二者的定义与使用
11.1 概述
有时需要将不同类型的数据组合成一个有机的整体以便于引用,如,
一个学生的学号,姓名,性别,年龄,成绩,
地址等,分别定义简单变量难以反映它们之间的联系,
应当将它们组成一个组合项,其中可以包含若干个类型不同的数据项,
C提供的结构体相当于 记录
struct student
{int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
关键字不能省略 结构体名,和标准类型名一样可以定义变量成员列表,每个成员又称分量或域,
成员名定名规则与变量同作为语句,必须以分号结束声明一个结构体类型的一般形式
struct 结构体名
{成员表列 };
类型名 成员名
11.2定义结构体类型变量的方法
1.先定义结构体类型再定义变量名
3.直接定义结构体类型变量
2.在定义类型的同时定义变量
11.3结构体类型变量的引用
不能将一个结构体变量为一个整体进行输入输出
只能对最低级的成员进行赋值或存取以及运算
对成员变量可以象普通变量一样进行各种运算
可以引用成员的地址,也可以引用结构体变量的地址说明
11.4 结构体变量的初始化
11.5结构体数组结构体数组与以前介绍的数值型数组不同之处在于每个数组元素都是一个结构体类型的数据
11.5.1 结构体数组的定义
11.5.2 结构体数组的初始化
11.5.3 举例,对候选人得票的统计程序,
例如
11.6指向结构体类型数据的指针一个结构体变量的指针就是该变量所占据的内存段的起始地址,可以设一个指向一个结构体变量的指针变量指向结构体变量的指针指向结构体数组的指针用指向结构体的指针作函数参数如上面已定义的结构类型 struct student,
可以用它来定义变量,
struct student student1,student2
变量名注意,
定义标准类型变量和定义结构体类型变量不同前者类型是已知的 (如 int,float),而后者要先定义类型后定义变量人们通常用一个符号常量代表一个结构体类型
#define STUDENT struct student
先声明结构体类型在定义变量名
struct student
{int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}student1,student2;
变量名列表在声明类型的同时定义变量定义的一般形式为:
struct 结构体名
{
成员表列
} 变量名表列
struct
{int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}student1,student2;
直接定义结构体类型变量说明,
1.类型与变量是不同的,变量可以赋值,存取和运算。对类型是不分配空间的,
2.对结构体中的成员,可以单独使用,它的作用与地位相当与普通变量,
3.成员也可以是一个结构体变量。如:
struct date
{ int month;
int day;
int year;
}
4.成员名可以与程序中的变量名相同
Struct student
{int num;
char name[20];
char sex;
int age;
struct date birthday;
char addr[30];
} student1,student2;
printf(“%d,%s,%c,%d,%f,%s\n”,student1);
引用方式,结构体变量名,成员名。如:
student1.num
结构体变量名 成员名成员运算符
(1)不能将结构体变量作为整体进行输入输出 。
如果成员本身又是一个结构体类型,则要用成员运算符一级一级地找到最低一级的成员
struct student
{int num;
char name[20];
char sex;
int age;
struct date birthday;
char addr[30];
}student1,student2;
struct date
{int month;
int day;
int year;
};
正确引用,
student1.num
student1.birthday.day
student2.score=student1.score;
sum=student1.score+student2.score;
student1.age+ +;
+ +student2.birthday.year;
“.”的优先级最高可以,
scanf(“%d”,&student1.num);
printf(“%o”,&student2);
不可以,
scanf(“%d%s%c%d%f%s”,&student1);
struct student
{long int num;
char name[20];
char sex;
char addr[30];
}a={89031,"Li Lin",'M',"123 Beijing
Road"};
main()
{printf(“No.:%ld\nname:%s\nsex:%c\naddr
ess:%s\n”,a.num,a.name,a.sex,a.addr);}
No.,89031
name:Li Lin
sex,M
address,123 Beijing Road
方法 1:
struct student
{int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
struct student stu[3];
方法 2:
struct student
{int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu[3];
方法 3:
struct
{int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu[3];
num name sex age score ddr
10101 LiLin M 18 87.5 103 Beijing Road
10102 Zhangyi M 19 99 130 Shanghai Road
10103 Wanger F 20 78.5 1010 Jinan Road
stu[0]
stu[1]
stu[2]
数组各元素在内存中连续存放
10101
“LiLin”
M?
18
87.5
“103 Beijing Road”
10102
“zhangyi”
M?
19
...
stu[0]
stu[1]
struct student
{long int num;
char name[20];
char sex;
char addr[30];
}stu[3]={{89031,”Li Lin”,?M?,”123
Beijing Road”},{10102,”Zhangyi”,…},
{10103,…}};
例 11.3 指向结构体变量的指针
#include <string.h>
main()
{struct student
{long num;
char name[20];
char sex;
float score;
};
struct student stu_1;
struct student *p;
p=&stu_1;
stu_1.num=89101;
Strcpy(stu_1.name,"Li Lin");
stu_1.sex='M';
stu_1.score=89.5;
printf ("No.,%ld\nname:%ssex:%c\nscore:%f\n",
stu_1.num,stu_1.name,stu_1.sex,stu_1.score);
printf ("No.,%ld\nname:%ssex:%c\nscore:%f\n",
(*p).num,(*p).name,(*p).sex,(*p).score);
}
三种等价形式,
1 结构体变量,成员名
2 (*p).成员名
3 p->成员名
struct student stu;
struct student *p;
p=&stu;
89101
“Li Lin”
M?
89.5
p stu.num
stu.name
stu.sex
stu.score
(*p).num
(*p).name
(*p).sex
(*p).score
p->num
p->name
p->sex
p->score
分析以下几种运算:
指向运算符
p->n
++p->n
p->n++ 使用值后加 1
先加 1后使用值
11.6.2指向结构体数组的指针例 11.2 指向结构体数组的指针
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}};
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);
} p
已有一个结构体数组,
struct student stu[4]={...};
再定义一个指针变量,
struct student *p;
p=stu;
如,for (p=stu;p<stu+3;p++)
printf(“%5d,%-20s,%2c,%4d”,p->num,
p->name,p->sex,p->age);
注,
先使 p指向数组 stu的起始地址,并于第一个循环输出 stu[0]的各个成员,
然后 p+1意味着增加的地址值为结构体类型数组 stu
的一个元素所占的字节数注意区别,
(++p)->num
(p++)->num
p已定义为指向 struct student类型的数据,
p=stu;正确 (数组名)
p=&stu.name;错误(不能指向结构体成员)
p=(struct student *)&stu.name
11.6.3 用结构体变量和指向结构体的指针作为函数参数有 3种方法,
1.用结构体变量成员作参数,如:
2.用结构体变量作实参,如:
主函数中,
...
print(stu.name);
...
被调函数中,
void print(char a[ ] )
{…}
主函数中,
...
print(stu);
...
被调函数中,
void print(a)
struct student a;
{…}
3,用指向结构体变量 (或数组 )的指针作实参,将结构体 (或数组 )的地址传 给形参,
主函数中,
...
print(&stu);
...
被调函数中,
void print(a)
struct student *a;
{…}
例 11.5 用结构体变量名作参数
#include <string.h>
#define FORMAT "%d\n%s\n%f\n%f%f\n"
struct student
{int num;
char name[20];
float score[3];
};
main()
{void print (struct student);
struct student stu;
stu.num = 12345;
strcpy(stu.name,"Li Li");
stu.score[0]=67.5;
stu.score[1]=89;
stu.score[2]=78.9;
print (stu);
}
void print(struct student stu)
{printf (FORMAT,stu.num,stu.name,stu.score[0],
stu.score[1],stu.score[2]);
printf ("\n");
}
例 11.6 改用指向结构体变量的指针作参数
#include <string.h>
#define FORMAT "%d\n%s\n%f\n%f%f\n"
struct student
{int num;
char name[20];
float score[3];
}stu={12345,"Li Li",67.5,89,78.6};
main()
{void print (struct student *);
print (&stu);
}
void print (struct student *p)
{ printf (FORMAT,p->num,p->name,
p->score[0],p->score[1],p->score[2]);
printf("\n");
}
把一个完整的结构体变量作为参数传递,虽然合法,但既费时又费空间,不如用指针效率高,
11.7用指针处理链表动态分配内存函数
1,void *malloc(unsigned int)
2,void *calloc(unsigned n,unsigned size);
3,void free(void *p)
11.8 共用体
11.8.1 共用体的概念
有时需要将几种不同类型的变量存放到同一段内存单元中,如,
将一个整型变量,一个字符型变量,一个实型变量放在同一个地址开始的内存单元中,
由于三个变量在内存中占的字节数不同,故使用覆盖技术,使三个变量互相覆盖,
整型字符型实型
1000
定义共用体类型变量的方法
先定义共用体类型再定义变量名
在定义类型的同时定义变量
直接定义共用体类型变量
11.8.2共用体变量的引用方式
先定义后使用,不能引用共用体变量,只能引用共用体变量中的成员,
如正确引用,a.i
a.ch
a.f
错误引用,printf(“%d”,a);
继续
union data
{int i;
char ch;
float f;
}a,b,c;
关键字 共用体名成员表列变量表列
union 共用体
{成员表列
} 变量表列在定义类型的同时定义变量
union data
{int i;
char ch;
float f;
};
union data a,b,c;
先定义共用体类型再定义变量名
union {int i;
char ch;
float f;
}a,b,c;
共用体与结构体形式相似,而含义不同,
结构体变量所占内存长度是各成员占的内存长度之和,共用体变量所占内存长度等于各成员中占的内存长度最长的,
共用体类型数据的特点,
(1)同一内存段可以用来存放几种不同类型的成员,但每一瞬时只能存放其中一种
(2)共用体变量中起作用的成员是最后一次存放的成员
a.i=1;
a.c='a';
a.f=1.5;
(3)共用体变量的地址和它的成员地址都是同一地址
例如,&a,&a.i,&a.c,&a.f表示的是同一地址
(4)不能对共用体变量名赋值,如 a=1
不能引用变量名来得到成员值,如 m=a
不能初始化,如,union
{int i;
char ch;
float f;
} a={1,'a',1.5};
(5)不能把共用体变量作为函数参数,也不能使函数带回共用体变量,但可以用指向共用体变量的指针
(6) 共用体类型可以出现在结构体类型定义中,也可以定义共用体数组,反之依然,
如果一个变量只有几种可能的值,可以定义为枚举类型,变量的值只限于列举出来的值的范围内,
enum weekday
{sun,mon,tue,wed,thu,fri,sat};
关键字 枚举类型名 枚举元素或枚举常量然后,可以定义变量,
enum weekday workday,week_end;
11.9枚举类型
也可以直接定义枚举变量
enum {sun,mon,tue,wed,thu,fri,sat} workday,week_end;
说明,
在 C编译中,对枚举元素按常量处理,不能赋值,
枚举元素作为常量,标识符并不代表什麽含义,但它们是有值的,C语言编译按顺序为,0,1,2,3… … 。
由上面定义,则 sun值为 0,mon的值为 1,...sat值为 6。
也可以改变枚举元素的值,如:
enum weekday {sun=7,mon=1,tue,wed,thu,fri,sat} day;
枚举值可以用来作判断比较
一个整数不能直接赋给一个枚举变量,如,
day=2;错误,因它们不属于同一类型,应先进行强制类型转换,
如,
workday=(enum weekday)2; 此相当于将序号为 2的枚举元素赋给 workday.表达式也可以,
例如
11.10 用 typedef 定义类型除了可以使用 C提供的标准类型名和、共用体、
指针、枚举类型外,还可以 typedef 定义新的类型名来代替已有的类型名。
typedef int INTEGER;
typedef float REAL;
int i,j;
float a;b;等价于
INTEGER i,j;
REAL a,b;
声明结构体类型:
typedef struct
{int month;
int day;
int year;
}DATE;
可以定义变量,
DATE birthday;
DATE *p;不要写成 struct DATE birthday
归纳定义一个 新的类型名的方法,
① 先按定义变量的方法写出定义体 (int i;)
② 将变量名换成新类型名 (将 i换成 COUNT)
③ 在最前面加 typedef(typedef int COUNT)
④ 然后用新类型名去定义变量如,typedef int NUM[100];
NUM n;
如,typedef char *STRING;
STRING p,s[10] ;
如,typedef int (*POINTER)();
POINTER p1,p2 ;
定义 NUM为整型数组类型定义 n为整型数组变量定义 STRING为字符指针类型
P为字符指针变量,s为指针数组定义 POINTER为指向返回整型值的函数的指针类型
P1,p2为 POINTER型指针变量
typedef int count;
COUNT i;
说明,
用 typedef可以定义各种类型名,但不能用来定义变量
用 typedef只是对已经存在的类型增加一个类型名,而没有创造出新类型
typedef与 #define有相似之处,但事实上二者不同,预编译只作简单的字符替换,typedef是在编译时处理的,它如同定义变量那样来定义一个类型
typedef有利于程序的移植,比如,我们要使用一类 4字节的变量。
typedef long integer对 4字节的机器
typedef int integer;
对 2字节的机器
C 语言程序设计例 11.2 3个候选人,输入得票人的名字,要求最后输出个人得票结果
#include "string.h"
struct person
{char name[20];
int count;
} leader[3]={"li",0,"zhang",0,"fun",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++;
}
printf ("\n");
for (i=0;i<3;i++)
printf
("%5s:%d\n",leader[i].name,leader[j].count);
}
例 11.13口袋中有红、黄、蓝、白、黑 5种颜色的球若干个。每次从口袋中取出 3个球,问得到 3种不同色的球的的可能取法,打印出每种组合的 3
种颜色。
main()
enum color {red,yellow,blue,
white,black};
enum color i,j,k,pri;
int n,loop;
n=0;
for (i=red;j<=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 1,pri=j; break;
case 1,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;
default,break;
}
}
printf ("\n");
}
}
printf
("\ntotal:%5d\n",n);
运行结果如下:
1 red yellow blue
2 red yellow white
3 red yellow black
……,……………..
58 Black white red
59 black white yellow
60 black white blue
total 60