宿舍管理系统——单链表+结构体实现入住、退房和查询功能(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、学生成绩信息管理
对学生的成绩信息进行管理,学生信息包括:学号、姓名、学期、每门课程的成绩、平均成绩、名次。实现:学生信息的录入;修改;删除和查询,按学期、学号、成绩不及格等查询。
要求:
- 要有菜单进行功能选择
- 各个功能要分到不同的函数来写
- 禁止使用goto语句
- 链表的各种操作要熟练掌握,会进行调试(调试我打算单独放一篇文章来讲,这篇主要讲怎么实现这个程序)
- 链表结点如果使用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~ 😀