820 likes | 939 Views
第十章 结构体与共用体. 10.1.1 结构体类型的定义. C 语言没有提供结构体类型,而是提供了结构体类型的定义方法,我们在使用时需要自己定义。 struct 结构体名 { 结构体成员列表 }; 在定义结构体成员列表时,成员的定义形式为: 类型名 成员名 ;. 举例. 我们定义一个描述商品信息的结构体类型。 struct goods { int number; char name[10]; float price; }; struct goods 就是结构体类型名 该结构体类型包含三个成员.
E N D
10.1.1 结构体类型的定义 • C语言没有提供结构体类型,而是提供了结构体类型的定义方法,我们在使用时需要自己定义。 struct 结构体名 { 结构体成员列表 }; • 在定义结构体成员列表时,成员的定义形式为: 类型名 成员名;
举例 • 我们定义一个描述商品信息的结构体类型。 struct goods { int number; char name[10]; float price; }; • struct goods就是结构体类型名 • 该结构体类型包含三个成员
10.1.2 结构体变量的定义 • (1)使用结构体类型名定义变量 struct goods { int number; char name[10]; float price; }; struct goods g1, g2 ;
numbe r name g1 price 存储空间示意图 • 各成员按定义顺序依次存放, • 成员的存储空间是相邻的。 • 成员number是整型,占4个字节; • 成员name是字符型数组,占10个字节 • 成员price是实型,占4个字节; • 变量g1和g2各占18个字节存储空间。
10.1.2 结构体变量的定义 • (2)定义结构体类型的同时定义变量 struct goods { int number; char name[10]; float price; } g1, g2 ; • 结构体变量g1和g2的定义直接跟在结构体类型struct goods的定义之后。
10.1.2 结构体变量的定义 • (3)直接定义结构体类型变量 struct { int number; char name[10]; float price; } g1, g2; • 在这种定义方式下,没有给该结构体类型命名,因此无法在其他位置定义该结构体类型的变量,也无法将它们用作函数参数。
10.1.2 结构体变量的定义 • (4)使用typedef • typedef可为一个已存在的数据类型定义一个类型名。 typedef int integer; • 为int类型指定一个新的类型名integer。 typedef float real; • 为float类型指定一个新的类型名real。 integer a, b; /*a,b为int类型变量*/ real c, d; /*c,d为float类型变量*/ • 注意:typedef并不引入一个新的数据类型,只是给已定义的数据类型指定一个同义词。
10.1.2 结构体变量的定义 typedef struct goods { int number; char name[10]; float price; } kind; • 为结构体类型struct goods起了一个新的名字kind。
可以使用struct goods去定义结构体变量,也可以使用kind去定义结构体变量。比如: struct goods g1; kind g2;
10.1.3 结构体变量的使用 • 在定义了结构体变量后,访问结构体各成员的语法格式为: 结构体变量名. 成员名 • 结构体变量的成员可以像普通变量一样进行各种运算和操作。例如: g1.number=10446; strcpy(g1.name, “apple”) g1.price=1.8; printf(“%d, %s”, g1.number, g1.name); scanf(“%f”,&g1.price);
10.1.3 结构体变量的使用 • 结构体变量不能作为一个整体进行输入输出等运算操作。 printf(“%d, %s, %f”, g1); /*这样是错误的*/
结构体变量初始化 • 结构体变量可以在定义的时候赋予初始值 struct goods { int number; char name[10]; float price; } g1={10446, “apple”, 1.8}; • 初始值用一对大括号括起来,并且必须按照结构体类型定义中成员的排列顺序依次给出每个初始值。
void main( ) { struct goods { int number; char name[10]; float price; } g1; printf(“请输入商品编号、名称、单价:\n”); scanf(“%d%s%f”,&g1.number, g1.name, &g1.price); if( strcmp(g1.name, “apple”)==0) g1.price*=0.8; printf(“打折后为: %d,%s,%f ”, g1.number, g1.name, g1.price); } • 【例10-1】从键盘读入一种商品的编号,名称和价格信息,如果输入的商品名称是 “apple”,则价格打8折。最后输出打折后的信息。
10.2 结构体数组 • 以超市购物系统为例,有了结构体变量之后可以方便地存储一种商品的多类信息,但超市商品种类有很多,如果为每一类商品都定义一个结构体变量也是很复杂的。这种情况我们可以使用结构体数组,相当于批量定义多个结构体变量。
10.2.1 结构体数组的定义 • (1)使用结构体类型名定义数组 struct goods { int number; char name[10]; float price; } struct goods g[10];
10.2.1 结构体数组的定义 • (2)定义结构体类型的同时定义数组 struct goods { int number; char name[10]; float price; }g[10];
10.2.1 结构体数组的定义 • (3)直接定义结构体类型数组 struct { int number; char name[10]; float price; }g[10];
10.2.1 结构体数组的定义 • (4)使用typedef typedef struct goods { int number; char name[10]; float price; } kind; kind g[10];
g[0] g[1] g[9] 结构体数组g的存储空间示意图 • 以上四种方法都可以定义一个结构体数组g,里面含有10个元素,g[0]、g[1]......g[9]。每个数组元素都是一个结构体,都含有三个成员,例如: g[0].number=10446; strcpy(g[0].name, “apple”); g[0].price=1.8; g[9].number=10442; strcpy(g[9].name, “orange”); g[9].price=1.5;
10.2.2 结构体数组的初始化 • 在定义结构体数组时可以给每个数组元素指 定初始值,此时需要将每个数组元素的成员的值 分别用大括号括起来。 struct goods { int number; char name[10]; float price; }g[3]={{10446, “apple”, 1.8}, {10447, “banana”, 2.6}, {10448, “pear”, 1.6}};
void main( ) • { • struct goods • { • int number; • char name[10]; • float price; • } g[10]; • int i, max, min; • float average=0.0; • for(i=0;i<10;i++) • { • printf("输入商品编号、名称、单价:\n"); • scanf("%d%s%f", &g[i].number, • g[i].name,g[i].price); • } • 【例10-2】统计商品信息
max=min=0; • for(i=0;i<10;i++) • { • if(g[i].price>g[max].price) • max=i; • if(g[i].price<g[min].price) • min=i; • average=average+g[i].price; • } • average=average/10; • printf("the max price is:%d,%s,%f\n", • g[max].number,g[max].name, g[max].price); • printf("the min price is:%d,%s,%f\n", • g[min].number,g[min].name, g[min].price); • printf("the average price is:%f\n", average); • }
10.3 结构体类型指针 • 定义方法: struct 结构体名 *指针变量名; struct goods *p ; • 指针p并未指向一个已存在的结构体,所以不能通过指针p访问结构体的内容。我们可以通过初始化或赋值操作让指针p指向一个结构体。 struct goods g1,*p =&g1; 或 struct goods g1,*p ; p=&g1;
10.3.1 指向结构体变量的指针 • 访问其指向的结构体变量成员的方法: • 方法一: (*指针变量名) . 结构体成员名 例如:(*p).number (*p).name • 注意:运算符“.”的优先级高于运算符“*”,因此*指针变量名必须用括号括起来。 • 方法二: 指针变量名->结构体成员名 例如:p->number p->name
p &g1 g1 • void main( ) • { • struct goods • { • int number; • char name[10]; • float price; • } g1, *p; • p=&g1; /*指针需指向某一个结构体变量后才能使用*/ • printf(“请输入商品编号、名称、单价:\n”); • scanf(“%d%s%f”,&p->number, p->name, &p->price); • if( strcmp(p->name, “apple”)==0 ) • p->price*=0.8; • printf(“打折后:%d,%s,%f\n”,(*p).number, (*p).name, (*p).price); • } • 【例10-3】将程序10-1改为通过指针变量对结构体进行操作。
p g[0] p+1 g[1] 10.3.2 指向结构体数组的指针 • 定义一个结构体类型的指针变量指向一个结构体数组,这样可以通过指针变量的算术运算来访问结构体数组中的每个元素。 struct goods g[10],*p; p=g; • 此时,指针变量指向的是结构体数组的第一个元素。
void main( ) { struct goods { int number; char name[10]; float price; }; struct goods g[10],*p,*pmax,*pmin; int i; float average=0.0; p=g; //指针p指向了数组的首地址 printf(“input information of the goods:\n”); for(i=0;i<10;i++) { scanf(“%d%s%f”, &p->number, p->name, &p->price); p++; } • 【例10-4】统计10种商品价格最高和最低的,以及商品的平均价格。要求使用结构体指针实现。
p=g; pmax=pmin=g; for(i=0;i<10;i++) { if(p->price>pmax->price) pmax=p; if(p->price<pmin->price) pmin= p; average=average+ p->price; p++; } average=average/10; printf(“the max price is:%d,%s,%f\n”, pmax->number, pmax->name, pmax->price); printf(“the min price is:%d,%s,%f\n”, pmin->number,pmin->name,pmin->price); printf(“the average price is:%f\n”, average); }
10.4 结构体与函数 • 通过函数参数在两个函数之间传递一个结构体通常有以下三种方式。 • 结构体变量的成员作函数实参 • 结构体变量作函数参数 • 指向结构体的指针作函数参数
10.4.1 结构体变量的成员作函数实参 • 结构体的一个或多个成员可作为独立的函数参数进行传递。 • 注意:如果结构体成员为数组或指针类型,将其作为实参,则实参与形参之间传递的数据为地址。实参和形参便指向同一个对象,被调函数执行时,可以通过形参访问其所指向的对象,如果被调函数利用形参修改了其所指向的对象的内容,实际上也就是实参所指向的对象的内容改变了。
10.4.1 结构体变量的成员作函数实参 #include <stdio.h> #include <string.h> struct student { int num; char name[20]; char sex; int score[3]; }; void fun(int num, char name[ ], char sex, int s[ ]) { num=10447; strcpy(name, "lisi"); sex='W'; s[0]=60; s[1]=60; s[2]=60; }
程序执行后输出结果为: 调用函数前数据为:10446,zhangsan,M,78,96,86 调用函数后数据为: 10446,lisi,M,60,60,60 void main( ) { struct student stu; stu.num=10446; strcpy(stu.name, "zhangsan"); stu.sex= 'M'; stu.score[0]=78; stu.score[1]=96; stu.score[2]=86; printf("调用函数前数据为:%d,%s,%c,%d,%d,%d\n", stu.num, stu.name, stu.sex, stu.score[0], stu.score[1], stu.score[2] ); fun(stu.num, stu.name, stu.sex, stu.score); printf("调用函数后数据为:%d,%s,%c,%d,%d,%d\n", stu.num, stu.name, stu.sex, stu.score[0],stu.score[1], stu.score[2] ); }
10.4.2 结构体变量作函数参数 • 可以用结构体变量作实参,对应的形参应该是相同类型的结构体变量。实参将各个成员的值传递给形参的对应成员。被调函数执行过程中,形参值发生了改变,实参值不会相应变化。
10.4.2 结构体变量作函数参数 • #include <stdio.h> • #include <string.h> • struct student • { • int num; • char name[20]; • char sex; • int score[3]; • }; • void fun(struct student stu) • { • stu.num= 10447; • strcpy(stu.name, “lisi”); • stu.sex=‘W’; • stu.score[0]=60; stu.score[1]=60; stu.score[2]=60; • }
void main() • { • struct student stu; • stu.num=10446; • strcpy(stu.name, “zhangsan”); • stu.sex= ‘M’; • stu.score[0]=78; stu.score[1]=96; stu.score[2]=86; • printf("调用函数前为:%d,%s,%c,%d,%d,%d\n", • stu.num, stu.name, stu.sex, stu.score[0], • stu.score[1], stu.score[2] ); • fun(stu); • printf("调用函数后为:%d,%s,%c,%d,%d,%d\n", • stu.num, stu.name, stu.sex, stu.score[0], • stu.score[1] , stu.score[2] ); • } • 程序执行后输出结果为: • 调用函数前数据为:10446,zhangsan,M,78,96,86 • 调用函数后数据为:10446,zhangsan,M,78,96,86 10.4.2 结构体变量作函数参数
10.4.3 指向结构体的指针作函数参数 • 在使用指向结构体的指针做实参时,对应的形参也应定义为结构体指针类型。实参传递给形参的是一个结构体的地址,实参和形参指向同一个结构体,在被调函数执行过程中,通过形参对该结构体做的任何改变,也相当于改变了实参所指向的结构体的值。
实参p &stu stu 形参p &stu 10.4.3 指向结构体的指针作函数参数 #include <stdio.h> #include <string.h> struct student { int num; char name[20]; char sex; int score[3]; }; void fun(struct student *p) { p->num= 10447; strcpy(p->name, “lisi”); p->sex=‘W’; p->score[0]=60; p->score[1]=60; p->score[2]=60; }
10.4.3 指向结构体的指针作函数参数 void main( ) { struct student stu, *p; stu.num=10446; strcpy(stu.name, “zhangsan”); stu.sex= ‘M’; stu.score[0]=78; stu.score[1]=96; stu.score[2]=86; p=&stu; printf("调用函数前为:%d,%s,%c,%d,%d,%d\n", stu.num, stu.name, stu.sex, stu.score[0], stu.score[1], stu.score[2] ); fun(p); /*或者fun(&stu);*/ printf("调用函数后为:%d,%s,%c,%d,%d,%d\n", stu.num, stu.name, stu.sex, stu.score[0], stu.score[1],stu.score[2] ); } 程序执行后输出结果为: 调用函数前数据为:10446,zhangsan,M,78,96,86 调用函数后数据为:10447,lisi,W,60,60,60
10.5 链表 • 链表是一种能够灵活扩展长度的动态数据结构 • 链表由多个结点通过指针的指向连接在一起,每个结点都有独立的存储空间,一个结点的存储空间是连续的,但多个结点的存储空间可以是不连续的。每个结点的存储空间可以分为两部分:数据域和指针域。数据域用于存放用户需要处理的数据,指针域用于存放下一个结点的地址。我们就可以从链表的第一个结点开始,依次访问链表中的每个结点。
10.5 链表 • 为了方便找到头结点,对于每个链表,我们都会定义一个称为“头指针”的指针变量,该指针变量内存放的是链表头结点的地址,也就是头指针指向链表的头结点。 • 链表的最后一个结点的指针域存放的是空指针,表示链表到此结束。空指针可以用C语言定义的符号常量NULL来表示(在C语言中符号常量NULL被定义为0)。
10.5 链表 • 以超市商品管理为例,使用链表来存放商品信息更为方便,因为商品的种类数目是不断变化的。 • 定义结构体类型如下: struct goods { char name[10]; float price; struct goods *next; };
10.5.1 静态链表 • 静态链表指的是链表中的各个结点都是在程序中事先定义好的,编译器会分配每个结点的存储空间,不需要在程序执行时动态分配结点存储空间。链表的长度是固定的。
struct goods { char name[10]; float price; struct goods *next; }; void main( ) { struct goods g1,g2,g3,*p,*head; strcpy(g1.name, ”apple”); g1.price=1.8; strcpy(g2.name, ”banana”); g2.price=2.6; strcpy(g3.name, ”pear”); g3.price=1.6; head=&g1; g1.next=&g2; g2.next=&g3; g3.next=NULL; p=head; while(p!=NULL) { printf(“%s,%.2f\n”,p->name,p->price); p=p->next; } } • 【例10-8】建立一个链表,用来存储苹果、香蕉、桃子这三种水果的名称和价格信息。
10.5.2 动态链表 • 动态链表是指链表中的各个结点的存储空间是在程序运行期间动态分配的,结点获得存储空间后才可以存储信息。结点数目即链表的长度是可以不断变化的。这样可以灵活的根据信息存储的需要添加或删除结点。 • C语言函数库提供了一些完成空间分配与回收的函数。
10.5.2.1 空间分配与回收函数 • (1)malloc函数 • 功能:在内存中分配一块大小为size字节的连续存储空间。 • 调用形式为: void * malloc(unsigned int size); • 当为新结点分配一块空间时,结点中存放的数据应该是结构体类型的,所以需要对返回值进行强制类型转换, struct goods *p; p=( struct goods * ) malloc( sizeof(struct goods) );
10.5.2.1 空间分配与回收函数 • (2)calloc函数 • 功能:在内存中分配n块,每块大小为size字节的连续空间 • 函数调用形式为: void * calloc(unsigned int n, unsigned int size);
10.5.2.1 空间分配与回收函数 • (3)free函数 • 功能:释放指针变量p所指向的内存块,使这块区域可以被其他数据使用。 • 函数调用形式为: void free(void *p);
10.5.2.2 建立和输出链表 • 使用malloc函数,依次为每个结点分配存储空间,并将这些结点通过指针连接起来形成一个链表,最后从链表的第一个结点开始输出每个结点的内容。
10.5.2.2 建立和输出链表 • 【例10-9】依次输入每种商品的名称和单价,当输入的商品名为“end”时,表示输入结束。利用输入的商品信息建立链表,然后输出链表的内容。 #include <stdio.h> #include <string.h> #include <malloc.h> struct goods { char name[10]; float price; struct goods *next; };