標籤:c語言 編程
序言
在整理檔案時偶然發現大一時的C語言大作業,故突然心血來潮想看看曾經花費一個星期的成果。
它雖然只有一個檔案,卻足足有829行,可見當時我下了多大功夫。回憶起當時作業的要求,大致也不過是實現一個可以增刪改記錄的學產生績管理系統,何至於要寫829行呢?找到源碼配備的文檔,想起那些我絞盡腦汁想出來的諸多花哨的功能,我不禁感慨:年輕就是能折騰啊!
而在我準備仔細拜讀時,我卻高興不起來了——我根本看不懂也不想看懂My Code!儘管我讀過很多很爛的代碼,但當發現自己曾經的驕傲之作其實是一片垃圾不堪入目時,我還是十分難受。
記得老師布置大作業的時候鼓勵我們多寫代碼,多添加功能,於是我就認為代碼越多越好,並添加了很多古怪的功能。而現在我很清楚:代碼不是拼數量的,而是拼品質的,功能設計也不是越豐富越好,為意義不大的功能耗費精力是不值得的。
好的代碼?
回顧我學習C的一點點經曆,感慨曾經有太多對程式設計語言的誤解:
- 狂熱於語言技巧 死記運算子優先順序,死鑽語言特性的行為就是走火入魔,語言是為我們服務的,好用的特性就用,不好用的特性應當避免。以前以為一個人知道越多的語言特性就越懂一門語言,現在才明白懂如何用一門語言比探究語言本身更重要。
- 誤以為代碼越難懂越好 曾以為只有自己能看懂的程式才算高大上,肆無忌憚地使用“
#”、“##”、“#@”預先處理運算子,濫用那些鮮為人知的宏替換的技巧。不僅降低了自己寫代碼的效率,還增加了別人閱讀的時間,到最後寫出的代碼連自己也看不懂。
- 盲目追求運行效率 曾參加智能車比賽,為了提高車速,誤以為變數名改短,數組展開成簡單變數,迴圈展開…等就能提高程式運行速度,簡直愚蠢之極。(想象力太豐富不一定是好事,要多看書,少瞎想)
- 不願分享代碼 曾以為代碼是自己辛勤勞動所得,如果給了別人就沒有價值了。現在才明白代碼就像一件藝術品,沒有人看它,它也就沒有價值了。
我想,寫代碼應當努力達到作詩般簡練優雅:
- 不追求平仄交替,只求抒發情感,表達思想
- 不追求辭藻華麗,只求通俗易懂,老嫗能解
這樣的好代碼才能被人們clone、star、傳誦。
重寫代碼!
萬千的感慨令我不禁神遊了一陣子,回過神,想起大四沒有課,也算是有點閑時間,於是想改改這份代碼。可當我發現幾乎所有地方都有毛病時且不想再看邏輯混亂的代碼時,我又改了主意,哎,還是重寫吧!
總算是把800多行改成200多行了,程式講解見我的Github部落格,希望對剛學完C語言的同學們有所協助。本人能力有限,如有前輩願意點撥,敬請在本文後評論,不勝感激!
最後附上代碼:
/* * Student Information Manager * https://baidut.github.io/ | Released under MIT license * Copyright (c) 2015 Zhenqiang.Ying <[email protected]> */#include <stdio.h>#include <stdlib.h>#include <string.h>#define MAX 100#define NAME_LEN 10typedef struct { char name[NAME_LEN]; unsigned int score[3]; unsigned int sum;} Stu_t;Stu_t stu[MAX];int num;Stu_t input_stu(void); // 通過提示引導使用者正確地輸入一條學生記錄,並返回這條記錄void output_stu(int i); // 根據數組索引列印出對應的學生資訊int find_stu(void); // 通過提示引導使用者尋找記錄,如果找到則顯示尋找到的記錄,並返回數組索引,否則返回-1void read_stu(const char *filename); // 根據檔案名稱讀入檔案中的學生資料void write_stu(const char *filename); // 將學生資料寫入到指定的檔案中int confirm(const char *words); // 請求使用者確認,輸入y或Y返回1,輸入n或N返回0,其他情況提示使用者重新輸入void safe_flush(FILE *fp); // safe_flush(stdin)清理多餘的輸入,防止影響下次擷取輸入int main(void) { const char *filename = "stud_info.txt"; read_stu(filename); // 匯入資料 printf("歡迎使用學產生績管理系統\n" "-當前已有%d條記錄,本程式限制%d條記錄\n", num, MAX); for (;;) { char choice; int i, j; printf( "-菜單:\t" "1.尋找\t" "2.追加\t" "3.刪除\t" "4.修改\t" "5.排序\t" "6.顯示\t" "7.儲存\t" "0.退出\n" "輸入序號:"); choice = getchar(); safe_flush(stdin); switch (choice) { case ‘1‘: // 尋找 find_stu(); break; case ‘2‘: // 追加 if (num < MAX) { stu[num] = input_stu(); output_stu(num); num++; puts("追加成功!"); } else puts("記錄已滿,無法追加!"); break; case ‘3‘: // 刪除 i = find_stu(); if (-1 != i) { if (confirm("確認刪除?")) { while (i++ != num) { stu[i-1] = stu[i]; } num--; puts("刪除成功!"); } } break; case ‘4‘: // 修改 i = find_stu(); if (-1 != i) { stu[i] = input_stu(); output_stu(i); puts("修改成功!"); } break; case ‘5‘: // 按照總分排序 for (i = 0; i < num-1; i++) // 冒泡排序中已經排好的 for (j=0; j < num-1-i; j++) // 遍曆沒排好的 if ( stu[j].sum < stu[j+1].sum ) { Stu_t t = stu[j]; stu[j] = stu[j+1]; stu[j+1] = t; } puts("排序成功!"); /* fall through */ // 完成排序後自動顯示 case ‘6‘: // 顯示 for (i = 0; i < num; i++) { printf("%4d:\t", i+1); output_stu(i); } break; case ‘7‘: // 儲存 write_stu(filename); break; case ‘0‘: // 退出 if (confirm("推出前儲存?(Y/N)")) { write_stu(filename); } exit(0); default: puts("沒有該序號!請重新輸入"); break; } } // END for return 0;}void safe_flush(FILE *fp){ int ch; while ( ch = fgetc(fp), ch != EOF && ch != ‘\n‘);}int confirm(const char *words) { char choice; puts(words); for (;;) { choice = getchar(); safe_flush(stdin); switch (choice) { default: puts("輸入非法,重新輸入!"); continue; case ‘Y‘: case ‘y‘: puts("已確認!"); return 1; case ‘N‘: case ‘n‘: puts("已取消!"); return 0; } }}Stu_t input_stu(void) { Stu_t ret; puts("依次輸入姓名、語文、數學、外語成績,空格分隔,成績要求是整數:"); while ( 4 != scanf("%s%d%d%d", ret.name, &ret.score[0], &ret.score[1], &ret.score[2])) { safe_flush(stdin); puts("輸入有誤,請重新輸入!"); } safe_flush(stdin); ret.sum = ret.score[0] + ret.score[1] + ret.score[2]; return ret;}void output_stu(int i) { if (i < 0 || i > MAX) return; printf("%s\t語:%d\t數:%d\t外:%d\t總分:%d\n", stu[i].name, stu[i].score[0], stu[i].score[1], stu[i].score[2], stu[i].sum);}int find_stu(void) { char name[NAME_LEN]; int i; printf("輸入姓名(限%d個字元):", NAME_LEN); scanf("%s", name); safe_flush(stdin); for (i = 0; i < num; i++) { if (0 == strcmp(stu[i].name, name)) { output_stu(i); return i; } } puts("沒有找到記錄!"); return -1;}void read_stu(const char *filename) { FILE *fp = fopen(filename, "rt"); if (NULL != fp) { fread(&num, sizeof(int), 1, fp); fread(stu, sizeof(Stu_t), num, fp); } fclose(fp);}void write_stu(const char* filename) { FILE *fp = fopen(filename, "wt"); if (NULL != fp) { fwrite(&num, sizeof(int), 1, fp); fwrite(stu, sizeof(Stu_t), num, fp); fclose(fp); puts("儲存成功!"); } else puts("開啟檔案失敗!");}
大四重寫大一的C語言大作業是一種怎樣的體驗