本程序的編譯和運行環境如下(如果有運行方面的問題歡迎在評論區留言,也歡迎直接加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~ 😀