100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > [C语言] 通讯录|静态 动态 文件 链表 多版本讲解

[C语言] 通讯录|静态 动态 文件 链表 多版本讲解

时间:2022-08-30 05:18:36

相关推荐

[C语言] 通讯录|静态 动态 文件 链表 多版本讲解

学校的期末小作业,相当于对我们本学期所学内容的一个总结。只要对标题所指内容有所了解即可轻松读懂本题解。下面我们按照要求一步步由浅入深地解决这个问题。

目录

静态版本

定义类型

添加

输出

查找

修改

删除

动态版本

定义类型

对静态版本的修改

添加

文件操作

文件加载

文件保存

链表

定义类型

初始化

添加

输出

查找

修改

删除

退出、保存文件、销毁

静态版本

定义类型

#define MAX 1000typedef struct student{int no; //学号char name[10]; //姓名int score; //成绩}student;typedef struct students{student data[MAX]; //创建int sz;//当前人数}students;

增删查改输出都需要知道当前人数,所以可以把结构体数组和人数再封装一个结构体。

初始化、打印菜单、选项

void menu(){printf(">>************************\n");printf(">>****1.添加 2.输出****\n");printf(">>****3.查找 4.修改****\n");printf(">>****5.删除 0.退出****\n");printf(">>************************\n");}enum option{退出,添加,输出,查找,修改,删除,};int main(){int input = 0;students stu = { 0 };do{menu();printf(">>请选择:");scanf("%d", &input);switch (input){case 添加:AddStu(&stu);break;case 输出:PrintStu(&stu);break;case 查找:SearchStu(&stu);break;case 修改:ModifyStu(&stu);break;case 删除:DelStu(&stu);break;case 退出:printf(">>退出\n");break;default:printf(">>选择错误,重新选择\n");break;}} while (input);return 0;}

以下关于各个功能的实现:

添加

首先判断是否已满,然后进行添加,每一次都添加在当前人数下标位置上,每添加一个sz++

void AddStu(students* pc){if (pc->sz == MAX){printf(">>已满,无法添加\n");return;}printf(">>请输入学号:");scanf("%d", &pc->data[pc->sz].no);printf(">>请输入姓名:");scanf("%s", pc->data[pc->sz].name);printf(">>请输入成绩:");scanf("%d", &pc->data[pc->sz].score);pc->sz++;printf(">>增加成功\n");}

输出

通过循环一个个输出即可

void PrintStu(const students* pc){int i;printf("%-5s\t%-10s\t%-5s\n", "学号", "姓名", "成绩");for (i = 0; i < pc->sz; i++){printf("%-5d\t%-10s\t%-5d\n", pc->data[i].no, pc->data[i].name, pc->data[i].score);}}

查找

由于修改和删除也需要用到查找功能,所以可以再封装一个函数,也可以再写个FindByName,FindByScore之类的函数,防止学号相同。找到返回下标,找不到返回-1,最后输出即可。

static int FindByNo(students* pc, int no){int i;for (i = 0; i < pc->sz; i++){if (pc->data[i].no == no)return i;}return -1;}void SearchStu(students* pc){int no;printf(">>请输入要查找的人的学号:");scanf("%d", &no);int pos = FindByNo(pc, no);if (pos == -1){printf(">>要查找的人不存在\n");return;}else{printf("%-5s\t%-10s\t%-5s\n", "学号", "姓名", "成绩");printf("%-5d\t%-10s\t%-5d\n", pc->data[pos].no, pc->data[pos].name, pc->data[pos].score);}}

修改

引用上个函数判断是否存在,然后输出。

void ModifyStu(students* pc){int no;printf(">>请输入要修改的人的学号:");scanf("%d", &no);int pos = FindByNo(pc, no);if (pos == -1){printf(">>要修改的人不存在\n");return;}else{printf(">>请输入学号:");scanf("%d", &pc->data[pos].no);printf(">>请输入姓名:");scanf("%s", pc->data[pos].name);printf(">>请输入成绩:");scanf("%d", &pc->data[pos].score);printf(">>修改成功\n");}}

删除

首先判断通讯录是否为空,然后查找。删除只要将其后面的信息依次往前挪即可,注意i<pc->sz-1,原本的最后一个不需要被覆盖,因为只要最后sz--了,以后也访问不到。

void DelStu(students* pc){int no, i;if (pc->sz == 0){printf(">>无人可删\n");return;}printf(">>请输入要删除人的学号:");scanf("%d", &no);int pos = FindByNo(pc, no);if (pos == -1){printf(">>要删除的人不存在\n");return;}for (i = pos; i < pc->sz-1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf(">>删除成功\n");}

静态版本最简单,但缺点很明显

大量的空间被浪费掉了。下面使用动态内存分配进行改造。

动态版本

定义类型

在类型定义方面,我们需要增加一个capacity变量记录当前最大容量,判断是否已满,是否需要扩容时需要用到。

typedef struct student{int no; //学号char name[10]; //姓名int score; //成绩}student;typedef struct students{student* data; //创建int sz;//当前人数int capacity; //当前最大容量}students;

自然地,对这个结构体变量不能简单的使用={0}进行初始化,而要写一个初始化函数:设置初始容量DEFAULT_SZ为3,每次扩容的增量INC_SZ为2。

#define DEFAULT_SZ 3#define INC_SZ 2void InitStu(students* pc){pc->data = (student*)malloc(DEFAULT_SZ * sizeof(student));if (pc->data == NULL){perror("InitStu");return;}pc->sz = 0;pc->capacity = DEFAULT_SZ;}

接下来思考功能方面有哪些需要改动

对静态版本的修改

添加

增容一般都发生在添加时,所以只需要修改添加函数即可,每次添加要判断是否已满,若已满,则使用realloc重新申请一块空间。

void AddStu(students* pc){if (pc->sz == pc->capacity){student* ptr = (student*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(student));if (ptr != NULL){pc->data = ptr;pc->capacity += INC_SZ;printf("增容成功\n");}else{perror("AddStu");printf(">>增加失败\n");return;}}printf(">>请输入学号:");scanf("%d", &pc->data[pc->sz].no);printf(">>请输入姓名:");scanf("%s", pc->data[pc->sz].name);printf(">>请输入成绩:");scanf("%d", &pc->data[pc->sz].score);pc->sz++;printf(">>增加成功\n");}

安全起见,退出时可以将其销毁。

void DestoryStu(students* pc){free(pc->data);pc->data = NULL;pc->sz = 0;pc->capacity = 0;}

其他函数可以照常运行。

文件操作

通讯录每次运行都不会保留上次的数据,所以需要文件操作将其保存到硬盘。

文件加载

每次运行都应加载上次保存的文件,也就是在初始化函数内调用LoadStu函数。

由于读文件时也要判断通讯录是否已满需要增容,所以对之前写过的直接封装函数void CheckCapacity(students* pc)

fread的返回值即为此次读到的元素个数,当未读到信息时while循环停止。

void CheckCapacity(students* pc){if (pc->sz == pc->capacity){student* ptr = (student*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(student));if (ptr != NULL){pc->data = ptr;pc->capacity += INC_SZ;printf("增容成功\n");}else{perror("AddStu");printf(">>增加失败\n");return;}}}void LoadStu(students* pc){FILE* pf = fopen("students.dat", "r");if (pf == NULL){perror("LoadStu");return;}//读文件student tmp = { 0 };while (fread(&tmp, sizeof(student), 1, pf)){CheckCapacity(pc);pc->data[pc->sz] = tmp;pc->sz++;}//关闭文件fclose(pf);pf == NULL;}

文件保存

通过循环一个个写进去即可。在退出时销毁前调用。

void SaveStu(students* pc){FILE* pf = fopen("students.dat", "w");if (pf == NULL){perror("SaveStu");return;}//写文件int i;for (i = 0; i < pc->sz; i++){fwrite(pc->data + i, sizeof(student), 1, pf);}//关闭文件fclose(pf);pf = NULL;}

其余不变。

链表

使用链表更加方便,无需考虑当前人数,是否已满和是否需要扩容。

定义类型

typedef struct student{int no; //学号char name[10]; //姓名int score; //成绩}student;typedef struct students{student data;struct students* next;}LinkList;

初始化

初始化如果没有数据就只会有一个头节点。下面加载文件采用后插法形成链表。

LinkList* InitStu(){LinkList* head;head = (LinkList*)malloc(sizeof(LinkList));if (head == NULL){return NULL;}head->next = NULL;LoadStu(head);return head;}void LoadStu(LinkList* head){FILE* pf = fopen("students.dat", "r");if (pf == NULL){perror("LoadStu");return;}//读文件student tmp = { 0 };LinkList* node;while (fread(&tmp, sizeof(student), 1, pf)){node = (LinkList*)malloc(sizeof(LinkList));if (node == NULL){return;}node->data = tmp;head->next = node;head = node;}head->next = NULL;//关文件fclose(pf);pf = NULL;}

添加

这里使用前插法,因为是头节点传参,如果采用后插法则不知道结尾在哪。

void AddStu(LinkList* head){LinkList* node = (LinkList*)malloc(sizeof(LinkList));if (node == NULL){return;}printf(">>请输入学号:");scanf("%d", &node->data.no);printf(">>请输入姓名:");scanf("%s", node->data.name);printf(">>请输入成绩:");scanf("%d", &node->data.score);node->next = head->next;head->next = node;printf(">>添加成功\n");}

输出

void PrintStu(LinkList* head){head = head->next;printf("%-5s\t%-10s\t%-5s\n", "学号", "姓名", "成绩");while (head != NULL){printf("%-5d\t%-10s\t%-5d\n", head->data.no, head->data.name, head->data.score);head = head->next;}}

查找

之前是返回下标,链表就返回节点的指针就好,未找到则返回NULL。

static LinkList* FindByNo(LinkList* head, int no){head = head->next;while (head != NULL){if (head->data.no == no){return head;}head = head->next;}return NULL;}void SearchStu(LinkList* head){int no;printf(">>请输入要查找人的学号:");scanf("%d", &no);LinkList* node = FindByNo(head, no);if (node == NULL){printf(">>要查找的人不存在\n");return;}else{printf("%-5s\t%-10s\t%-5s\n", "学号", "姓名", "成绩");printf("%-5d\t%-10s\t%-5d\n", node->data.no, node->data.name, node->data.score);}}

修改

void ModifyStu(LinkList* head){int no;printf(">>请输入要修改人的学号:");scanf("%d", &no);LinkList* node = FindByNo(head, no);if (node == NULL){printf(">>要修改的人不存在\n");return;}else{printf(">>请输入学号:");scanf("%d", &node->data.no);printf(">>请输入姓名:");scanf("%s", node->data.name);printf(">>请输入成绩:");scanf("%d", &node->data.score);}printf(">>修改成功\n");}

删除

由于链表的删除需要知道前一节点的位置,所以查找时不能使用FindByNo函数。

void DelStu(LinkList* head){int no;printf(">>请输入要删除人的学号:");scanf("%d", &no);LinkList* last = head;while (head != NULL){head = head->next;if (head->data.no == no){break;}last = last->next;}if (head == NULL){printf(">>要删除的人不存在\n");return;}last->next = head->next;free(head);printf(">>删除成功\n");}

退出、保存文件、销毁

void SaveStu(LinkList* head){FILE* pf = fopen("students.dat", "w");if (pf == NULL){perror("SaveStu");return;}//写文件head = head->next;while (head != NULL){fwrite(&head->data,sizeof(student),1,pf);head = head->next;}//关闭文件fclose(pf);pf = NULL;}void DestoryStu(LinkList* head){LinkList* last = head;while (head != NULL){head = head->next;free(last);last = head;}}

主函数

int main(){int input = 0;LinkList* pt = InitStu();do{menu();printf(">>请选择:");scanf("%d", &input);switch (input){case 添加:AddStu(pt);break;case 输出:PrintStu(pt);break;case 查找:SearchStu(pt);break;case 修改:ModifyStu(pt);break;case 删除:DelStu(pt);break;case 退出://保存到文件SaveStu(pt);//销毁学生们DestoryStu(pt);printf(">>退出\n");break;default:printf(">>选择错误,重新选择\n");break;}} while (input);return 0;}

本人较懒,通讯录的排序以及动态版本的空间的减容下次再搞吧。

另外这道题高度综合本学期课内知识,还是要把各个知识点吃透。寒假继续加油!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。