C语言基础学习day10
结构体与共用体
概述
定义一个结构的一般形式为:
struct 结构名 { 成员表列 };
成员表列由若干个成员组成,每个成员都是该结构的一个组成部分。对每个成员也必须作类型说明,其形式为:
类型说明符 成员名;
struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }
定义结构体类型变量的方法
可以采取以下3种方法定义结构体类型变量:
(1)先声明结构体类型再定义变量名
例如:
struct student student1, student2;
| | | |
类型名 结构体 变量名 变量名
定义了student1和student2为struct student类型的变量,即它们具有struct student类型的结构.
struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; } student1, student2
在定义了结构体变量后,系统会为之分配内存单元
例如: student1和student2在内存中各占 ? 个字节。 ( 4 + 20 + 1 + 4 + 4 + 30 = 67 )
(2)在声明类型的同时定义变量 这种形式的定义的一般形式为:
struct 结构体名 { 成员表列
}变量名表列;
(3) 直接定义结构体类型变量 其一般形式为:即不出现结构体名。
struct { 成员表列 }变量名表列;
嵌套结构
首先定义一个结构date,由month(月)、day(日)、year(年) 三个成员组成。
在定义并说明变量 boy1 和 boy2 时,其中的成员birthday被说明为data结构类型。成员名可与程序中其它变量同名,互不干扰。
struct date { int month; int day; int year; };
struct { int num; char name[20]; char sex; struct date birthday; float score; }boy1, boy2;
结构体变量的引用
在定义了结构体变量以后,当然可以引用这个变量。但应遵守以下规则:
(1) 不能将一个结构体变量作为一个整体进行输入和输出。
例如: 打印student1的各个变量的值。
可以这样吗? printf(″%d,%s,%c,%d,%f,%\n″,student1); 不可以
正确引用结构体变量中成员的方式为:
结构体变量名.成员名
student1.num: 表示student1变量中的num成员,即student1的num(学号)项。
可以对变量的成员赋值,例如:student1.num=100;
“.”是成员(分量)运算符,它在所有的运算符中优先级最高,因此可以把student1.num作为一个整体来看待。上面赋值语句的作用是将整数100赋给student1变量中的成员num。
(2) 如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。
对上面定义的结构体变量student1, 可以这样访问各成员:
student1.num
student1.birthday.month
(3) 对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。 例如:
student2.score = student1.score; sum = student1.score + student2.score; student1.age++;
++student2.age;
(4) 可以引用结构体变量成员的地址,也可以引用结构体变量的地址。
#include <stdio.h> void main() { struct student { int num; char *name; char sex; float score; } boy1; boy1.num = 007; boy1.name = "Jane"; printf("The address of struct is %o :\n", &boy1 ); printf("The address of num is %o :\n", &boy1.num ); }
名称和第一个变量的地址是相等的
输出结果
但不能用以下语句整体读入结构体变量:
scanf(″%d,%s,%c,%d,%f,%s″,&student1);
结构体变量的地址主要用作函数参数,传递结构体变量的地址。
结构体变量的初始化
#include <stdio.h> void main() { struct student /*定义结构*/ { int num; char *name; char sex; float score; }boy1, boy2 = { 102, "Jane", 'M', 98.5 }; boy1 = boy2; printf("Number = %d\nName = %s\nScore = %d\n", boy1.num, boy1.name, boy1.score); printf("\n\n"); printf("Number = %d\nName = %s\nScore = %d\n", boy2.num, boy2.name, boy2.score); }
结构体数组
一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。
如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。
结构体数组与以前介绍过的数值型数组不同之处在于每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员(分量)项。
定义结构体数组
和定义结构体变量的方法相仿,只需说明其为数组即可。例如:
struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }; struct student student[3];
也可以这样:
struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }student[3];
结构体数组的初始化
与其他类型的数组一样,对结构体数组可以初始化。例如:
struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }stu[2]= { {101,″LiLin″,′M′,18,87.5,″Beijing″},
{102,″Zhang″,′F′,19,99,″Shanghai″} };
当然,数组的初始化也可以用以下形式:
struct student { int num; … }; struct student str[]{{…},{…},{…}};
即先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。
例题:对候选人得票的统计程序。设有3个候选人,每次输入一个得票的候选人的名字,要求最后输出各人得票结果。
#include <stdio.h> #include <string.h> #include <stdlib.h> #define NUM 4 struct person { char name[20]; int count; }candidate[NUM] = { {"小甲鱼", 0}, {"苍井空", 0}, {"松岛枫", 0}, {"莫丁丁", 0} }; char *winner(); void main() { int i, j; char candidate_name[20]; printf("欢迎进入良好公民评选投票系统:() \n\n"); printf("候选人有: 小甲鱼, 苍井空, 松岛枫, 莫丁丁(路人甲)\n\n"); for( i=1; i <= 10; i++ ) { printf("第 %2d 位投票, 请写下支持的候选人名字: ", i); scanf("%s", candidate_name); for( j=0; j < NUM; j++ ) { if( 0 == strcmp(candidate_name, candidate[j].name) ) { candidate[j].count++; } } } printf("\n"); for( i=0; i < 4; i++ ) { printf("%s 同学得票数为: %d\n", candidate[i].name, candidate[i].count ); } printf("\n"); printf("本次投票活动的胜利者的: %s", winner() ); printf("\n"); system("pause"); } char *winner() { int i =0 , winner = i; for( i=1; i < NUM; i++ ) { if( candidate[winner].count < candidate[i].count ) { winner = i; } } return candidate[winner].name; }
指向结构体类型数据的指针
一个结构体变量的指针就是该结构体变量所占据的内存段的起始地址。
可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。
指针变量也可以用来指向结构体数组中的元素。
结构指针变量说明的一般形式为:
struct 结构名 *结构指针变量名
例如,在前面的例题中定义了stu这个结构,如要说明一个指向stu的指针变量pstu,可写为:
struct stu *pstu;
当然也可在定义stu结构时同时说明pstu。与前面讨论的各类指针变量相同,结构指针变量也必须要先赋值后才能使用。
赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。
如果boy是被说明为stu类型的结构变量,则:
pstu=&boy
是正确的。
pstu=&stu
是错误的。
因为,结构名和结构变量是两个不同的概念,不能混淆。结构名只能表示一个结构形式,编译系统并不对它分配内存空间。
只有当某变量被说明为这种类型的结构时,才对该变量分配存储空间。
因此上面&stu这种写法是错误的,不可能去取一个结构名的首地址。有了结构指针变量,就能更方便地访问结构变量的各个成员。
其访问的一般形式为:
(*结构指针变量).成员名
或为:
结构指针变量->成员名
例如:
(*pstu).num
或者:
pstu->num
示例:
#include <stdio.h> struct stu { int num; char *name; char sex; float score; } boy1 = {102, "Fishc", 'M', 78.5}; void main() { struct stu *pstu; pstu = &boy1; printf("Number = %d\nName = %s\n", boy1.num, boy1.name); printf("Sex = %c\nScore = %f\n\n", boy1.sex, boy1.score); printf("Number = %d\nName = %s\n", (*pstu).num, (*pstu).name); printf("Sex = %c\nScore = %f\n\n", (*pstu).sex, (*pstu).score); printf("Number = %d\nName = %s\n", pstu->num, pstu->name); printf("Sex = %c\nScore = %f\n\n", pstu->sex, pstu->score); }
输出结果
结构指针变量作函数参数
将一个结构体变量的值传递给另一个函数,有3个方法:
(1)用结构体变量的成员作参数
(2)用结构体变量作实参
(3)用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参
例题:有一个结构体变量stu,内含学生学号、姓名和3门课程的成绩。通过调用函数print中将它们输出。
要求: 先用结构体变量作函数参数
#include <stdio.h> #include <string.h> struct student { int num; char *name; float score[3]; }; void print(struct student); void main() { struct student stu; stu.num = 8; stu.name = "Fishc.com!"; stu.score[0] = 98.5; stu.score[1] = 99.0; stu.score[2] = 99.5; print( stu ); } void print( struct student stu ) { printf("\tnum : %d\n", stu.num); printf("\tname : %s\n", stu.name); printf("\tscore_1 : %5.2f\n", stu.score[0]); printf("\tscore_2 : %5.2f\n", stu.score[1]); printf("\tscore_3 : %5.2f\n", stu.score[2]); printf("\n"); }
改用指向结构体变量的指针作实参: example02.c
#include <stdio.h> #include <string.h> struct student { int num; char name[20]; float score[3]; }; void print(struct student *); void main() { struct student stu; stu.num = 8; strcpy(stu.name, "Fishc.com!"); stu.score[0] = 98.5; stu.score[1] = 99.0; stu.score[2] = 99.5; print( &stu ); } void print( struct student *p ) { printf("\tnum : %d\n", p -> num); printf("\tname : %s\n", p -> name); printf("\tscore_1 : %5.2f\n", p -> score[0]); printf("\tscore_2 : %5.2f\n", p -> score[1]); printf("\tscore_3 : %5.2f\n", p -> score[2]); printf("\n"); }
动态存储分配
例如:int a[n]; 用变量表示长度,想对数组的大小作动态说明,这是错误的。
但是在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。
以对于这种问题,用数组的办法很难解决!
为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。
常用的内存管理函数有以下三个:
1. 分配内存空间函数 malloc、calloc
2. 释放内存空间函数 free
malloc函数
函数原型为
void *malloc(unsigned int size);
其作用是在内存的动态存储区中分配一个长度为size的连续空间(size是一个无符号数)。
此函数的返回值是一个指向分配域起始地址的指针(类型为void)。
如果此函数未能成功地执行(例如内存空间不足),则返回空指针(NULL)。
calloc函数
函数原型为
void *calloc(unsigned n, unsigned size);
其作用是在内存的动态存储区中分配n个长度为size的连续空间。 函数返回一个指向分配域起始地址的指针;
如果分配不成功,返回NULL。
用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。
free函数
函数原型为
void free(void *p);
其作用是释放由p指向的内存区,使这部分内存区能被其他变量使用。 p是最近一次调用calloc或malloc函数时返回的值。 free函数无返回值。
链 表
什么是链表?
链表是一种常见的重要的数据结构,是动态地进行存储分配的一种结构。
链表的组成:
头指针:存放一个地址,该地址指向第一个元素
结点:用户需要的实际数据和链接节点的指针
#include <stdio.h> struct student { long num; float score; struct student *next; }; void main() { struct student a, b, c, *head; a.num = 10101; a.score = 89.5; b.num = 10103; b.score = 90; c.num = 10107; c.score = 85; head = &a; a.next = &b; b.next = &c; c.next = NULL; do { printf("%ld %5.1f\n", head->num, head->score); head = head->next; }while( head != NULL ); }
建立动态链表
所谓建立动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系。
根据下面的分析写一函数建立一个含有学生(学号,成绩)数据的单向动态链表。 (约定:我们约定学号不会为零,如果输入的学号为0,则表示建立链表的过程完成,该结点不应连接到链表中。)
#include <stdio.h> #include <malloc.h> #include <stdlib.h> #define LEN sizeof(struct student) // student结构的大小 struct student *creat(); //创建链表 void print(struct student *head); //打印链表 struct student { int num; float score; struct student *next; }; int n; //全局变量,用来记录存放了多少数据。 void main() { struct student *stu; stu = creat(); print( stu ); printf("\n\n"); system("pause"); } struct student *creat() { struct student *head; struct student *p1, *p2; p1 = p2 = (struct student *)malloc(LEN); // LEN是student结构的大小 printf("Please enter the num :"); scanf("%d", &p1->num); printf("Please enter the score :"); scanf("%f", &p1->score); head = NULL; n = 0; while( 0 != p1->num ) { n++; if( 1 == n ) { head = p1; } else { p2->next = p1; } p2 = p1; p1 = (struct student *)malloc(LEN); printf("\nPlease enter the num :"); scanf("%d", &p1->num); printf("Please enter the score :"); scanf("%f", &p1->score); } p2->next = NULL; return head; } void print(struct student *head) { struct student *p; printf("\nThere are %d records!\n\n", n); p = head; if( NULL != head ) { do { printf("学号为 %d 的成绩是: %f\n", p->num, p->score); p = p->next; }while( NULL != p ); } }