1.64k likes | 1.8k Views
C 程 序 设 计. 江南大学控制科学与工程研究中心 张正道 (wxzzd@hotmail.com). 第 11 章 结构体与共用体. 结构体. 共用体. 枚举类型. 用 typedef 定义类型. Tc 工具. 11.1 结构类型变量的定义. 有时需要将不同类型的数据组合成一个有机的整体以便于引用 , 如 : 一个学生的学号 , 姓名 , 性别 , 年龄 , 成绩 , 地址等 . 分别定义简单变量难以反映它们之间的联系。学生的成绩、姓名、学号等是一组逻辑相关的数据,孤立地考虑这些属性,将导致操作的不便或逻辑错误。
E N D
C 程 序 设 计 江南大学控制科学与工程研究中心 张正道(wxzzd@hotmail.com)
第11章 结构体与共用体 结构体 共用体 枚举类型 用typedef定义类型 Tc工具
11.1 结构类型变量的定义 • 有时需要将不同类型的数据组合成一个有机的整体以便于引用,如: 一个学生的学号,姓名,性别,年龄,成绩,地址等.分别定义简单变量难以反映它们之间的联系。学生的成绩、姓名、学号等是一组逻辑相关的数据,孤立地考虑这些属性,将导致操作的不便或逻辑错误。 • 应当将它们组成一个组合项,其中可以包含若干个类型不同的数据项.
解决以上问题的方法就是引入结构体类型,将逻辑相关的数据有机组合在一起,称之为结构体。C提供的结构体相当于记录解决以上问题的方法就是引入结构体类型,将逻辑相关的数据有机组合在一起,称之为结构体。C提供的结构体相当于记录 • “结构”是一种构造类型,它是由若干“成员”组成的。 每一个成员可以是一个基本数据类型或者又是一个构造类型。 结构既是一种“构造”而成的数据类型, 那么在说明和使用之前必须先定义它,也就是构造它。如同在说明和调用函数之前要先定义函数一样。
一. 结构体类型的定义 结构体类型的一般定义形式为: struct 结构体类型名 { 数据成员列表; }; 用户命名的标识符 定义结构体类型的标识符 结构体类型定义的结束符
成员表由若干个成员组成, 每个成员都是该结构的一个组成部分。对每个成员也必须作类型说明,其形式为: 类型说明符 成员名; • 成员名的命名应符合标识符的书写规定。
例:一个学生的数据信息包含有学号、姓名、性别、年龄、成绩、住址,可将其定义为一个结构体类型:例:一个学生的数据信息包含有学号、姓名、性别、年龄、成绩、住址,可将其定义为一个结构体类型: struct student { long ID; /*学生学号*/ char name[10]; /*学生姓名*/ char sex; /*学生性别*/ int age; /*学生年龄*/ float score; /*学生成绩*/ char addr[30]; /*学生住址*/ }; 结构体类型定义仅仅是定义了一个特定的复合数据类型,描述了这一类型数据的公共属性,在程序中使用该结构体类型的具体对象,还需要说明这种类型的变量。
关键字不能省略 结构体名,和标准类型名一样可以定义变量 struct student{long ID; char name[10]; char sex; int age; float score; char addr[30]; }; 成员列表,每个成员又称分量或域.成员名定名规则与变量同 作为语句,必须以分号结束
stu1 ID(4字节) name …… 共10个字节 sex(1字节) 共51个字节 age(2字节) score(4字节) addr 共30个字节 …… stu2 ID …… 图:结构体变量的存储结构 11.2定义结构体类型变量的方法 结构体类型变量定义的 一般形式: struct 结构体类型名 结构体变量名;
1. 先定义结构体类型再定义结构体变量 struct student { long ID; char name[10]; char sex; int age; float score; char addr[30]; }; struct student stu1, stu2;
2. 定义结构体类型的同时定义变量 struct student { long ID; char name[10]; char sex; int age; float score; char addr[30]; }stu1,stu2;
3. 直接定义结构体变量 struct { long ID; char name[10]; char sex; int age; float score; char addr[30]; }stu1,stu2; 结构体变量的三种形式可以任意选用。但在不同函数中定义说明同一类型的结构体变量时,用第三种方法不太方便,一般用第一种和第二种定义形式。
4.结构体类型的嵌套 结构体类型的嵌套是指结构体的成员是一个结构体类型。 若定义学生信息为结构体,其成员分别为:学号、姓名、性别、出生年月、成绩。其中出生年月包括出生的年、月、日三个数据,这些数据可以用另一个结构体类型表示。 例如,定义student结构体。 (1)先定义date结构体: struct date {int year; int month; int day; }; (2)再定义student结构体: struct student { long ID; char name[10]; char sex; struct date birthday; float score; };
说明: 1.类型与变量是不同的, 变量可以赋值,存取和运算。对类型是不分配空间的. 2.对结构体中的成员,可以单独使用,它的作用与地位相当与普通变量. 3.成员也可以是一个结构体变量。如: struct date { int month; int day; int year; } 4.成员名可以与程序中的变量名相同 struct date birthday; char addr[30]; } student1,student2; Struct student {int num; char name[20]; char sex; int age;
1.结构成员的使用 使用结构变量时, 往往不作为一个整体来使用。除了允许具有相同类型的结构变量相互赋值以外, 一般对结构变量的使用,包括赋值、输入、输出、 运算等都是通过结构变量的成员来实现的。 表示结构变量成员的一般形式是: 结构变量名.成员名 例如: boy1.num 即第一个人的学号 boy2.sex 即第二个人的性别
11.3结构体类型变量的引用 一. 结构体类型变量的引用 使用结构变量时, 往往不作为一个整体来使用。除了允许具有相同类型的结构变量相互赋值以外, 对一个结构体类型变量的引用包括赋值、输入、输出、 运算等都是通过引用它的每一个结构成员来实现的。 结构体变量不能作为一个整体进行输入输出,只能对其成员分别输出 。
引用一个结构体变量的成员有两种方法: 结构体变量名、指向结构体的指针变量 引用运算符有两个: . -> 其中,“->”为结构体指针运算符, 结构体成员运算符“.”在所有运算符中优先级最高. 如果成员本身又是一个结构则必须逐级找到最低级的成员才能使用。例如: boy1.birthday.month 即第一个人出生的月份成员可以在程序中单独使用,与普通变量完全相同。
用结构体变量名引用其成员的一般形式: 结构体变量名.成员名 其中,“.”称为结构体成员运算符,将结构体变量名与成员名连接起来,它具有最高级别的优先级。 结构体变量可以单独引用其成员,也可作为一个整体引用,还可以引用结构体变量或成员的地址。 1. 单独引用结构体变量的成员 struct clock { int hour,minute,second;}; struct date today; today.year=2004; today.month=4; today.day=12; today.time.hour=16; today.time.minute=47; today.time.second=15; struct date { int year, month, day; struct clock time; };
2. 结构体变量作为一个整体引用 结构体变量不可以作为整体进行输入输出,但可以作为函数的参数或返回值而被整体引用,也可以将一个结构体变量作为一个整体赋给另一个具有相同类型的结构体变量。 struct date { int year, month, day;}; struct date nextday(day) struct date day; {struct date temp; ... return(temp); } 函数nextday的形参day为结构体类型,它将整体接受同类型实参的值
3. 引用结构体变量的地址或成员的地址 引用结构体变量的成员地址,要在结构体成员引用的前面再加“&”运算符. 结构体变量a的成员t赋值: scanf(”%d”,&a.t); 引用结构体变量的地址,在结构体变量的前面直接加“&”: printf("%X",&a);
二.结构变量的赋值 结构变量的赋值就是给各成员赋值。可用输入语句或赋值语句来完成。
【例】给结构变量赋值并输出其值。 main() { struct stu { int num; char *name; char sex; float score; } boy1,boy2; boy1.num=102; boy1.name="Zhang ping"; printf("input sex and score\n"); scanf("%c %f",&boy1.sex,&boy1.score); boy2=boy1; printf("Number=%d\nName=%s\n",boy2.num, boy2.name); printf("Sex=%c\nScore=%f\n",boy2.sex,boy2.score); }
本程序中用赋值语句给num和name两个成员赋值,name是一个字符串指针变量。用scanf函数动态地输入sex和score成员值,然后把boy1的所有成员的值整体赋予boy2。最后分别输出boy2 的各个成员值。本例表示了结构变量的赋值、输入和输出的方法。
说明 • 不能将一个结构体变量为一个整体进行输入输出 • 只能对最低级的成员进行赋值或存取以及运算 • 对成员变量可以象普通变量一样进行各种运算 • 可以引用成员的地址,也可以引用结构体变量的地址
11.4 结构体变量的初始化 结构体变量可以在说明的同时初始化。 struct clock { int hour,minute,second;}; struct date { int year,month,day; struct clock time; }; struct date today={2004,4,12,17,4,30}; struct date today={2004,4,12,{17,4,30}};
本例中,boy2,boy1均被定义为外部结构变量,并对boy1作了初始化赋值。在main函数中,把boy1的值整体赋予boy2, 然后用两个printf语句输出boy2各成员的值。
【例】外部结构变量初始化。 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\naddress:%s\n”,a.num,a.name,a.sex,a.addr);} No. :89031 name:Li Lin sex: M address: 123 Beijing Road
【例】静态结构变量初始化。 main() { struct stu /*定义结构变量*/ { int num; char *name; char sex; float score; }boy2,boy1={102,"Zhang ping",'M',78.5}; boy2=boy1; printf("Number=%d\nName=%s\n",boy2.num, boy2.name); printf("Sex=%c\nScore=%f\n",boy2.sex,boy2.score); } 本例是把boy1,boy2都定义为局部的 结构变量作初始化赋值。
11.5结构体数组 结构体数组与以前介绍的数值型数组不同之处在于每个数组元素都是一个结构体类型的数据 11.5.1结构体数组的定义 方法和结构变量相似,只需说明它为数组类型即可。例如: 1. 先定义结构体类型,后定义结构体数组 2. 结构体数组与结构体类型同时定义 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]; 方法1: struct student{int num;char name[20];char sex;int age;float score;char addr[30];};struct student stu[3];
11.5.2结构数组的初始化 对结构数组可以作初始化赋值,例如: struct stu { int num; char *name; char sex; float score; }boy[5]={{101,"Li ping","M",45}, {102,"Zhang ping","M",62.5}, {103,"He fang","F",92.5}, {104,"Cheng ling","F",87}, {105,"Wang ming","M",58}; } 当对全部元素作初始化赋值时,也可不给出数组长度。
struct person {long ID; char name[10]; char sex; struct date birthdate; char department[30]; float salary; char address[30]; }employee[]= {{1001,”张山”,’M’,1990,3,5,”计算中心”,545,”长沙”}, {1002,”李红”,’F’,1989,10,1,”信息学院”,643,”武汉”}, {1003,”王武”,’M’,1987,4,15,”人事处”,745,”广州”}};
employee[1] employee[0] employee[2] 1003 1002 1001 ID(4字节) ID(4字节) ID(4字节) "李红" "张山" "王武" name(10字节) name(10字节) name(10字节) 'M' 'F' 'M' sex(1字节) sex(1字节) sex(1字节) 1989 1990 1987 birthdate(date类型,6字节) birthdate(date类型,6字节) birthdate(date类型,6字节) 10 3 4 共85个字节 共85个字节 共85个字节 1 15 5 department(30个字节) department(30个字节) department(30个字节) "计算中心" "信息学院" "人事处" salary(4字节) salary(4字节) salary(4字节) 745 545 643 address(30个字节) address(30个字节) address(30个字节) "广州" "长沙" "武汉" 图 结构体数组的存储结构
结构体数组元素的引用 一个结构体数组元素相当于一个结构体变量,元素成员的访问使用数组元素的下标来实现。 结构体数组元素成员的访问形式: 结构体数组名[元素下标].结构体成员名 employee[0].ID=1001; 可以将一个结构体数组元素整体赋给同一结构体数组的另一个元素,或赋给同一结构体类型变量。 employee[1]=employee[2]; 与结构体变量一样,结构体数组元素也不能作为整体进行输入输出,只能以单个成员的形式实现。
【例】计算学生的平均成绩和不及格的人数。 struct stu { int num; char *name; char sex; float score; }boy[5]={ {101,"Li ping",'M',45}, {102,"Zhang ping",'M',62.5}, {103,"He fang",'F',92.5}, {104,"Cheng ling",'F',87}, {105,"Wang ming",'M',58} }; main() { int i,c=0; float ave,s=0; for(i=0;i<5;i++) { s+=boy[i].score; if(boy[i].score<60) c+=1; } printf("s=%f\n",s); ave=s/5; printf("average=%f\ncount =%d\n",ave,c); }
本例程序中定义了一个外部结构数组boy,共5个元素, 并作了初始化赋值。在main函数中用for语句逐个累加各元素的score 成员值存于s之中,如score的值小于60(不及格)即计数器C加1, 循环完毕后计算平均成绩,并输出全班总分,平均分及不及格人数。
【例】建立同学通讯录 #include"stdio.h" #define NUM 3 struct mem { char name[20]; char phone[10]; }; main() { struct mem man[NUM]; int i; for(i=0;i<NUM;i++) { printf("input name:\n"); gets(man[i].name); printf("input phone:\n"); gets(man[i].phone); } printf("name\t\t\tphone\n\n"); for(i=0;i<NUM;i++) printf("%s\t\t\t%s\n",man[i]. name,man[i].phone); }
本程序中定义了一个结构mem,它有两个成员name和phone 用来表示姓名和电话号码。在主函数中定义man为具有mem 类型的结构数组。在for语句中,用gets函数分别输入各个元素中两个成员的值。然后又在for语句中用printf语句输出各元素中两个成员值。
例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.6指向结构体类型数据的指针 当结构体很大时,结构体变量的整体赋值效率是相当低的。 结构体变量名就是分配给该变量的内存空间的起始地址,我们可以利用指向结构体变量的指针,实现在不同程序段对同一内存区域的结构体变量的数据成员执行各种操作。
一. 指向结构体变量的指针 指向结构体变量的指针也称为结构体指针,它保存了结构体变量的存储首地址。 1. 结构体指针的定义形式: struct 结构体类型名 *指针变量名; struct student stu,*p; p=&stu;
2. 结构体变量成员的三种访问方法 (1)结构体变量.成员名 stu.ID (2)(*结构体指针).成员名 (*p).ID (3)结构体指针->成员名 p->ID 结构体指针运算符“->” 结构体指针->结构体成员
例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 stu.numstu.namestu.sexstu.score (*p).num(*p).name(*p).sex(*p).score p->nump->namep->sexp->score p 分析以下几种运算: p->n p->n++ 使用值后加1 ++p->n 先加1后使用值
二. 指向结构体数组的指针 对于已定义的结构体数组,若用一个变量来存放该结构体数组在内存中的首地址,则该变量为指向结构体数组的指针变量。 例如,定义结构体类型person和结构体指针变量p。 struct person { char name[10]; int age; }; struct person *p,s,boy[3]={”Zhang”, 18,”Wang”,20,”Li”,17}; p=boy; 定义了结构体数组boy和结构体指针变量p,且p指向数组boy的首地址。
注意: 将结构体变量与结构体数组的首地址赋给结构体指针的不同之处: p=&s ; /*s为结构体变量*/ p=boy; /*boy为结构体数组,boy为数组的首地址*/ 结构体指针也可以指向结构体数组的一个元素,这时结构体指针变量的值是该结构数组元素的首地址。 若要将结构体成员的地址赋给结构体指针p,则必须使用强制类型转换操作,转换形式为: p=(struct 结构体类型名 *)&结构体数组元素.成员名
例:指向结构体数组的指针 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");