宿舍管理系统——单链表+结构体实现入住、退房和查询功能(C语言版)

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评论区留言,也欢迎直接加QQ:2961439733,备注博客园或CSDN即可):

  • 编辑工具:Dev-C++(版本:5.11.0.0)
  • 编译器:TDM-GCC 4.9.2 64-bit Release
  • 代码生成语言标准:ISO C99

演示及讲解视频链接:https://www.bilibili.com/video/BV1BC4y1a78W

老规矩,源码放在文章末尾了(源码上传到CSDN的话会被后台调整积分

好了开始进入正题,这次又双叒叕是我那位朋友的题目,不过已经是最后一题了。

课题四:信息管理系统

利用链表编写下列程序(二选一)

1、宿舍管理软件

用C语言为学生宿舍管理人员编写一个宿舍管理软件。设某宿舍有:101,102,201,202四个房间,每个房间可住学生<=4人,链表存储结构:学号、姓名、房间号、后续指针,按房间号有序,实现学生的入住、退房和查询,按给定学号、姓名、房号查询。

2、学生成绩信息管理

对学生的成绩信息进行管理,学生信息包括:学号、姓名、学期、每门课程的成绩、平均成绩、名次。实现:学生信息的录入;修改;删除和查询,按学期、学号、成绩不及格等查询。

要求:

  1. 要有菜单进行功能选择
  2. 各个功能要分到不同的函数来写
  3. 禁止使用goto语句
  4. 链表的各种操作要熟练掌握,会进行调试(调试我打算单独放一篇文章来讲,这篇主要讲怎么实现这个程序
  5. 链表结点如果使用malloc动态分配空间,需要释放

  看完题目我果断选择了第一题,不但好做,而且算是练手吧,因为第二题在我大一C语言课程设计时已经做过类似的了。那么来简单分析一下这个题如何下手:

首先先画出一个大致的流程图:

  接下来就是实现功能1~n,我们确定一下要用到的数据结构。按要求采用链表实现,结点是记录住宿信息的结构体:

  struct DORMITORY {
    int num; //房间号
    char id[15]; //学号
    char name[20]; //姓名
    struct DORMITORY* next;//指针域
  };

  这里我还采用了设计数据库表时的一些思想,比如主键。用学号来标识唯一的一个结构体,避免出现一个人住多个寝室的情况。这里由于题目限制,最多只会有16名学生,所以采用大小确定的二维数组进行保存,用于核对学号是否已经存在。下面再来看看我们要实现的功能。

   功能不多,主要分为入住、退房和查询三大类。入住即添加一条新的住宿信息,也就是新建链表结点;退房意味着删除一条住宿信息,即从链表里删除一个节点;至于查找嘛,这几乎是所有的管理类程序都绕不开的一个功能,在这里表现为从链表中找到符合条件的节点。它们分别对应着三种对数据的基本操作——增、删、查。还有一种是修改,题目里没要求我也就没实现,各位看官如果有兴趣可以自行实现。接下来我们进入到代码的具体实现环节,其中字符串的比较与复制用到了库函数strcmp和strcpy。

  首先是主页面:采用一个死循环,不断接受输入来执行各种功能(其余展示页面同理

 1 void menu() { //主菜单
 2     char t;
 3     int flag = 1;
 4     while(flag) {
 5         system("cls"); //清屏
 6         printf("+--------------------+\n");
 7         printf("|    宿舍管理系统    |\n");
 8         printf("+--------------------+\n");
 9         printf("|  【1】入住 管理    |\n");
10         printf("|                    |\n");
11         printf("|  【2】退房 管理    |\n");
12         printf("|                    |\n");
13         printf("|  【3】信息 查询    |\n");
14         printf("|                    |\n");
15         printf("|  【4】使用 说明    |\n");
16         printf("|                    |\n");
17         printf("|  【5】退出 系统    |\n");
18         printf("+--------------------+\n");
19         t=getch();    //不回显输入
20         switch(t) {
21             case '1':
22                 checkIn();   //入住管理
23                 break;
24             case '2':
25                 checkOut();  //退房管理
26                 break;
27             case '3':
28                 menu_query();//信息查询
29                 break;
30             case '4':
31                 direction(); //使用说明 
32                 break;
33             case '5':
34                 printf("\n感谢您的使用,再见( ̄︶ ̄)↗");
35                 destroy();  //销毁链表,释放空间 
36                 flag = 0;   //结束程序
37                 break;
38             default:
39                 break;
40         }
41     }
42 } 

   接下来我们依次实现主菜单里包含的功能:

  • 入住管理:
 1 void checkIn() { //登记入住信息 
 2     char t;
 3     while(head->num > 0) {//还有空余房间,继续循环 
 4         system("cls");    //清屏 
 5         dormitory node = create();//新建一个节点 
 6         if(node != NULL){ //创建成功 
 7             head->num -= 1;          //剩余房间数减 1 
 8             node->next = head->next; //每次新结点的next指向头结点的next 
 9             head->next = node;       //让头结点指向新建结点 
10         }else{ //创建失败 
11             printf("学号已存在,请重新进行添加操作!");
12             break;
13         }
14         printf("\n+--------------------+");
15         printf("\n|    是否继续添加    |");
16         printf("\n+--------------------+");
17         printf("\n|【1】是      【2】否|");
18         printf("\n+--------------------+");
19         t = getch();
20         if(t == '1')
21             continue;
22         else
23             break;
24     }
25     if(head->num == 0)//人数已满 
26         printf("\n宿舍房间人数已满,无法继续入住!");
27     printf("\n即将返回主菜单……"); 
28     Sleep(1500);     //暂停1.5秒后返回主菜单 
29 }

  这里我们解释一下头插法的实现过程:

  1.首先建立一个头结点:

  2.然后新建结点p拷贝head的next指针:

  3.最后更改head的next的指针指向:

  这种方式建立起来的链表是逆序的(相对于创建的先后顺序),不过不影响我们这个程序的功能实现。

  • 退房管理:
 1 void checkOut() { //退房处理,按学号
 2     char id[15];
 3     int flag = 0; 
 4     dormitory p = head->next;
 5     system("cls"); //清屏 
 6     print(); //打印出全部信息供选择
 7     //按学号的好处是唯一,不会删除同一房间号或者重名学生的信息,数据库里学号相当于主键  
 8     printf("请输入要进行此操作的学生学号(如果是误触,请输入esc确认返回):\n");
 9     scanf("%s", id);
10     if(!strcmp(id, "esc\0")){
11         p =NULL;
12         flag = 1; 
13     } 
14     for(; p != NULL; p = p->next){
15         if(!strcmp(p->id, id)){
16             del(p);      //找到对应学号,删除
17             head->num += 1;//剩余房间数加 1 
18             printf("退房办理成功!"); 
19             flag = 1;//把标志改为1 
20         }
21     } 
22     if(!flag)
23         printf("学号不存在!");
24     printf("\n即将返回主菜单……"); 
25     Sleep(1500); //暂停1.5秒后返回主菜单 
26 }
  • 信息查询(以按学号查询为例,其他的查询把代码里!strcmp(p->id, id)这个判断条件更改一下即可):
 1 void findById() { //按学号查询 
 2     char t, id[15]; 
 3     int flag = 0; 
 4     dormitory p = head->next;
 5     system("cls"); //清屏 
 6     printf("请输入要查询的学生学号:\n");
 7     scanf("%s", id);
 8     printf("\n+-------------------------------+\n"); 
 9     printf("|         住宿信息(全)          |\n");
10     printf("+-------------------------------+\n");
11     printf("| 宿舍号 |   学   号   | 姓  名 |\n");
12     printf("+-------------------------------+\n");
13     for(; p != NULL; p = p->next){ //输出与目标学号一致的住宿信息
14         if(!strcmp(p->id, id)){
15             flag = 1;
16             printf("|  %4d  | %11s | %6s |\n", p->num, p->id, p->name);
17         }
18     }
19     printf("+-------------------------------+\n");
20     if(!flag)
21         printf("\n未找到相关信息(>﹏<)");
22     while((t = getch()) != 27);//只有按下ESC键才会中断循环并返回 
23 }

  核心流程里的代码就这些,其余的部分请参考源码:

  1 #include<stdio.h>
  2 #include<conio.h>
  3 #include<stdlib.h>
  4 #include<string.h>
  5 #include<windows.h>
  6 
  7 #define N sizeof(struct DORMITORY)//宏定义,N为结构体变量所占内存的大小 
  8 
  9 struct DORMITORY {
 10     int num;  //房间号
 11     char id[15];      //学号
 12     char name[20];    //姓名
 13     struct DORMITORY* next;//指针域
 14 };
 15 //使用 typedef 关键字来定义自己习惯的数据类型名称
 16 //此处的意思为用dormitory来代替struct DORMITORY* 
 17 typedef struct DORMITORY* dormitory;
 18 
 19 char set[16][15];//学号集合,避免出现学号重复
 20 dormitory head;     //链表头节点,不用来保存住宿记录,方便建立链表(头插法) 
 21 
 22 void menu();           //主菜单
 23 void menu_query();  //信息查询菜单
 24 void direction();    //使用说明 
 25 void checkIn();        //入住 
 26 void checkOut();       //退房 
 27 void print();        //打印全部信息 
 28 void findAll();        //查看全部信息
 29 void findById();       //按学号查询 
 30 void findByName();     //按姓名查询 
 31 void findByNum();      //按宿舍号查询
 32 void del(dormitory node);    //删除一条住宿记录
 33 void destroy();                //销毁链表 
 34 int repeated(char id[15]);     //判断学号是否重复 
 35 dormitory create();         //创建一条住宿记录 
 36 
 37 int main() {
 38     //初始化链表
 39     head = (dormitory)malloc(N);
 40     //利用结构体中的学号变量记录剩余房间总数 
 41     head->num = 16; 
 42     //利用字符数组变量id记录四个房间的剩余房间数,依次是101、102、201、202 
 43     head->id[0] = '4';
 44     head->id[1] = '4';
 45     head->id[2] = '4';
 46     head->id[3] = '4';
 47     head->next = NULL;
 48     menu();
 49     return 0;
 50 }
 51 
 52 void menu() { //主菜单
 53     char t;
 54     int flag = 1;
 55     while(flag) {
 56         system("cls"); //清屏, 然后输出新的内容
 57         printf("+--------------------+\n");
 58         printf("|    宿舍管理系统    |\n");
 59         printf("+--------------------+\n");
 60         printf("|  【1】入住 管理    |\n");
 61         printf("|                    |\n");
 62         printf("|  【2】退房 管理    |\n");
 63         printf("|                    |\n");
 64         printf("|  【3】信息 查询    |\n");
 65         printf("|                    |\n");
 66         printf("|  【4】使用 说明    |\n");
 67         printf("|                    |\n");
 68         printf("|  【5】退出 系统    |\n");
 69         printf("+--------------------+\n");
 70         t=getch();    //不回显输入
 71         switch(t) {
 72             case '1':
 73                 checkIn();     //入住管理
 74                 break;
 75             case '2':
 76                 checkOut();     //退房管理
 77                 break;
 78             case '3':
 79                 menu_query();//信息查询
 80                 break;
 81             case '4':
 82                 direction(); //使用说明 
 83                 break;
 84             case '5':
 85                 printf("\n感谢您的使用,再见( ̄︶ ̄)↗");
 86                 destroy();  //销毁链表,释放空间 
 87                 flag = 0;    //结束程序
 88                 break;
 89             default:
 90                 break;
 91         }
 92     }
 93 }
 94 
 95 void menu_query() { //查询菜单
 96     char t;
 97     int flag = 1;
 98     while(flag) {
 99         system("cls"); //清屏, 然后输出新的内容
100         printf("+---------------------+\n");
101         printf("|      信息 查询      |\n");
102         printf("+---------------------+\n");
103         printf("|  【1】全体  查询    |\n");
104         printf("|                     |\n");
105         printf("|  【2】学号  查询    |\n");
106         printf("|                     |\n");
107         printf("|  【3】姓名  查询    |\n");
108         printf("|                     |\n");
109         printf("|  【4】房间号查询    |\n");
110         printf("+---------------------+\n");
111         t=getch();  //不回显输入
112         switch(t) {
113             case '1':
114                 findAll();     //查询全部
115                 break;
116             case '2':
117                 findById();     //按学号查询
118                 break;
119             case '3':
120                 findByName();//按姓名查询
121                 break;
122             case '4':
123                 findByNum(); //按宿舍号查询
124                 break;
125             case 27:
126                 flag = 0;     //返回主菜单
127                 break;
128             default:         //屏蔽其他输入
129                 break;
130         }
131     }
132 }
133 
134 int repeated(char id[15]){ //检查学号是否已经存在 
135     int i;
136     for(i = 0; i < 16; ++i){
137         if(!strcmp(id, set[i]))//如果学号已经存在,返回1
138             return 1;
139     }
140     return 0;
141 }
142 
143 dormitory create() { //创建一条入住信息
144     dormitory record = (dormitory)malloc(N);//申请空间 
145     //输入学号
146     printf("请输入学号:\n");
147     scanf("%s", record->id); 
148     //判重  
149     if(repeated(record->id))
150         return NULL;
151     strcpy(set[16 - head->num], record->id);//将不重复的学号记录在set数组 
152     //输入姓名
153     printf("请输入姓名:\n");  
154     scanf("%s", record->name);
155     //确定要分配的房间号 
156     if(head->id[0] > '0') {
157         record->num = 101;
158         head->id[0] -= 1; 
159     } else if(head->id[1] > '0') {
160         record->num = 102;
161         head->id[1] -= 1;
162     } else if(head->id[2] > '0') {
163         record->num = 201;
164         head->id[2] -= 1;
165     } else {
166         record->num = 202;
167         head->id[3] -= 1;
168     }
169     //指针指向 
170     record->next = NULL; 
171     return record;
172 }
173 
174 void del(dormitory node){ //删除一条住宿记录
175     int num = node->num, i = 0;
176     dormitory m = node->next;
177     dormitory p = head->next;
178     //set数组保留的对应学号删除
179     for(i = 0; i < 16; ++i){
180         if(!strcmp(node->id, set[i])){//找到学号,置空 
181             strcpy(set[i], "\0");
182             break; 
183         }
184     }
185     //把对应的房间数修改
186     if(num == 101)
187         head->id[0] += 1;
188     else if(num == 102)
189         head->id[1] += 1;
190     else if(num == 201)
191         head->id[2] += 1;
192     else
193         head->id[3] += 1;
194     //删除结点,释放空间 
195     if(m != NULL){ 
196         // node不是最后一个结点
197         //把node下一个结点的内容拷贝到自身,然后释放它下一个结点占用的内存 
198         node->num = m->num;
199         strcpy(node->id, m->id);
200         strcpy(node->name, m->name);
201         node->next = m->next;
202         free(m);
203     }else{//node是最后一个结点,释放node占用的内存 
204         m = head;
205         for(; p != NULL; m = p, p = p->next)
206             if(!strcmp(p->id, node->id))
207                 break;
208         m->next = NULL;
209         free(p);
210     }
211 } 
212 
213 void destroy(){ //销毁链表 
214     dormitory p = NULL;
215     dormitory q = head;
216     while(q != NULL) {
217         p = q;        //指向结构体所在内存地址
218         q = q->next;//找到下一块结构体所在内存地址 
219         free(p);    //释放空间 
220     }
221 } 
222 
223 void checkIn() { //登记入住信息 
224     char t;
225     while(head->num > 0) {//还有空余房间,继续循环 
226         system("cls");       //清屏 
227         dormitory node = create();//新建一个节点 
228         if(node != NULL){ //创建成功 
229             head->num -= 1;             //剩余房间数减 1 
230             node->next = head->next; //每次新结点的next指向头结点的next 
231             head->next = node;            //让头结点指向新建结点 
232         }else{ //创建失败 
233             printf("学号已存在,请重新进行添加操作!");
234             break;
235         }
236         printf("\n+--------------------+");
237         printf("\n|    是否继续添加    |");
238         printf("\n+--------------------+");
239         printf("\n|【1】是      【2】否|");
240         printf("\n+--------------------+");
241         t = getch();
242         if(t == '1')
243             continue;
244         else
245             break;
246     }
247     if(head->num == 0)//人数已满 
248         printf("\n宿舍房间人数已满,无法继续入住!");
249     printf("\n即将返回主菜单……"); 
250     Sleep(1500);     //暂停1.5秒后返回主菜单 
251 }
252 
253 void checkOut() { //退房处理,按学号
254     char id[15];
255     int flag = 0; 
256     dormitory p = head->next;
257     system("cls"); //清屏 
258     print(); 
259     //按学号的好处是唯一,不会删除同一房间号或者重名学生的信息,数据库里学号相当于主键  
260     printf("请输入要进行此操作的学生学号(如果是误触,请输入esc确认返回):\n");
261     scanf("%s", id);
262     if(!strcmp(id, "esc\0")){
263         p =NULL;
264         flag = 1; 
265     } 
266     for(; p != NULL; p = p->next){
267         if(!strcmp(p->id, id)){
268             del(p);      //找到对应学号,删除
269             head->num += 1;//剩余房间数加 1 
270             printf("退房办理成功!"); 
271             flag = 1;//把标志改为1 
272         }
273     } 
274     if(!flag)
275         printf("学号不存在!");
276     printf("\n即将返回主菜单……"); 
277     Sleep(1500); //暂停1.5秒后返回主菜单 
278 }
279 
280 void direction() { //使用说明 
281     char t; 
282     while(1){
283         system("cls"); //清屏 
284         printf("+------------------------------------------+\n");
285         printf("|              使  用  说  明              |\n");
286         printf("+------------------------------------------+\n");
287         printf("| 1. 每个操作都有对应的'【】'按键提示      |\n");
288         printf("|                                          |\n");
289         printf("| 2. 如无特殊提示,按'ESC'键返回上一个菜单 |\n");
290         printf("|                                          |\n");
291         printf("| 3. 预祝您使用愉快!o(* ̄▽ ̄*)o          |\n");
292         printf("+------------------------------------------+\n");
293         t = getch();
294         if(t == 27)//若输入为ESC键则中断循环,返回主菜单 
295             break;
296     } 
297 }
298  
299 void print() { //展示全部信息  
300     int i = 0; //记录共有几条信息 
301     dormitory p = head->next;
302     system("cls"); //清屏 
303     printf("+-------------------------------+\n"); 
304     printf("|          住宿信息(全)         |\n");
305     printf("+-------------------------------+\n");
306     printf("| 宿舍号 |   学   号   | 姓  名 |\n");
307     printf("+-------------------------------+\n");
308     for(; p != NULL; p = p->next, ++i) //输出全部信息 
309         printf("|  %4d  | %11s | %6s |\n", p->num, p->id, p->name);
310     printf("+-------------------------------+\n");
311     printf("|         共有%2d条信息          |\n", i);    
312     printf("+-------------------------------+\n"); 
313 }
314 
315 void findAll(){ //查询全部 
316     char t;
317     print();
318     while((t = getch()) != 27);//只有按下ESC键才会中断循环并返回
319 } 
320 
321 void findById() { //按学号查询 
322     char t, id[15]; 
323     int flag = 0; 
324     dormitory p = head->next;
325     system("cls"); //清屏 
326     printf("请输入要查询的学生学号:\n");
327     scanf("%s", id);
328     printf("\n+-------------------------------+\n"); 
329     printf("|         住宿信息(全)          |\n");
330     printf("+-------------------------------+\n");
331     printf("| 宿舍号 |   学   号   | 姓  名 |\n");
332     printf("+-------------------------------+\n");
333     for(; p != NULL; p = p->next){ //输出与目标学号一致的住宿信息
334         if(!strcmp(p->id, id)){
335             flag = 1;
336             printf("|  %4d  | %11s | %6s |\n", p->num, p->id, p->name);
337         }
338     }
339     printf("+-------------------------------+\n");
340     if(!flag)
341         printf("\n未找到相关信息(>﹏<)");
342     while((t = getch()) != 27);//只有按下ESC键才会中断循环并返回 
343 }
344 
345 void findByName() { //按姓名查询 
346     char t, name[15]; 
347     int flag = 0; 
348     dormitory p = head->next;
349     system("cls"); //清屏 
350     printf("请输入要查询的学生姓名:\n");
351     scanf("%s", name);
352     printf("\n+-------------------------------+\n"); 
353     printf("|         住宿信息(姓名)        |\n");
354     printf("+-------------------------------+\n");
355     printf("| 宿舍号 |   学   号   | 姓  名 |\n");
356     printf("+-------------------------------+\n");
357     for(; p != NULL; p = p->next){ //输出与目标学号一致的住宿信息
358         if(!strcmp(p->name, name)) {
359             flag = 1;
360             printf("|  %4d  | %11s | %6s |\n", p->num, p->id, p->name);
361         }
362     }
363     printf("+-------------------------------+\n");
364     if(!flag)
365         printf("\n未找到相关信息(>﹏<)");
366     while((t = getch()) != 27);//只有按下ESC键才会中断循环并返回 
367 }
368 
369 void findByNum() { //按宿舍号查询 
370     char t;
371     int num, i = 0;
372     int flag = 0;
373     dormitory p = head->next;
374     system("cls"); //清屏 
375     printf("请输入要查询的宿舍号:\n");
376     scanf("%d", &num);
377     printf("\n+-------------------------------+\n"); 
378     printf("|         住宿信息(宿舍)        |\n");
379     printf("+-------------------------------+\n");
380     printf("| 宿舍号 |   学   号   | 姓  名 |\n");
381     printf("+-------------------------------+\n");
382     for(; p != NULL; p = p->next){ //输出与目标学号一致的住宿信息
383         if(p->num == num){
384             i += 1;
385             printf("|  %4d  | %11s | %6s |\n", p->num, p->id, p->name);
386         }
387     }
388     printf("+-------------------------------+\n");
389     printf("|         共有%2d条信息          |\n", i);    
390     printf("+-------------------------------+\n");
391     while((t = getch()) != 27);//只有按下ESC键才会中断循环并返回 
392 }
宿舍管理系统

   希望你能在理解本程序的基础上完成文章开头提到的学生成绩信息管理,如果能在此基础上利用文件保存数据,相信你的水平能更上一层楼!码文不易,看到这里如果对你有帮助的话不妨点个赞支持一波再走吧,3Q~ 😀

posted @ 2020-06-14 17:06  小柒w  阅读(2097)  评论(0编辑  收藏  举报