花了幾天時間寫了一個比較簡單的項目,也算是自己做的第一個小項目。想的很簡單,寫的時候有些地方寫的還是很費勁,寫的時候沒有辦法聯網,現在把它放在Github中,電腦裏也不用留備份了,順便熟悉一下Git的用法,作爲菜鳥,深知路漫漫其修遠兮。
Github項目地址https://github.com/guangxyou/Simple.Lottery.Ticket.System
寫了個項目總結,放在這裏吧。
彩票管理系統項目
項目需求
詳見docx文檔
整體框架
詳見pdf文檔
文件結構
1.兩個子目錄
a)數據文件(隱藏admin文件)
b)界面文件
2.源程序文件(6個C文件,7個頭文件)
a)mainsys.c
b)user_list.c .h
c)lott_list.c .h
d)admin_operate.c .h
e)user_list.c .h
f)main_menu.c .h
g)common.h
代碼結構 接近2000行代碼
1.兩條單鏈表
a)用戶鏈 user_list.c
b)彩票鏈 lott_list.c
2.兩種用戶
a)admin
b)users
3.一個C文件對應一個H文件
common.h通用函數,頭文件都包含這個頭文件
4. 5個結構體
typedef struct global //全局變量
{
int g_issue; //期號
float g_jackpot; //獎池餘額
}GLO;
typedef struct user //用戶鏈表
{
int uid;
char name[NAME_LEN];
char passwd[PASS_LEN];
float balance;
struct user *next;
}USER, *pUser;
typedef struct lott //彩票鏈表
{
bool sweep_flag; //0沒有開獎,1已開獎
bool win_flag; //0沒有中獎,1中獎
char name[NAME_LEN]; //購買彩票用戶的姓名
int issue; //彩票期號
int uid; //購買彩票用戶的UID
int lott_num; //彩票號碼
int multiple; //注數
int book_amount; //購買彩票的金額=multiple*BASE1
struct lott *next;
}LOTT, *pLott;
typedef struct w_histoty //歷史中獎信息
{
char name[NAME_LEN];
int issue;
int lott_num;
int uid;
int win_amount;
}WINHIS;
typedef struct input //登錄時結構體
{
char name[NAME_LEN];
char passwd[PASS_LEN];
}INPUT;
函數:
1.開獎(兩條鏈表,一個開獎函數完成一個鏈表的功能,將中獎信息全部寫進文件,查詢時直接讀取文件)
void cash_award(int win_num, GLO *pglo, pUser puHead, pLott plHead); //admin_operate.c
2.餘額排序
void sort_balance(pUser puHead); //admin_operate.c
3.模糊查找
void fuzzy_lookup(int magic_num, char *magic_str, bool search_flag,
pUser puHead, pLott plHead); //admin_operate.c
4.搜索顯示函數(動態顯示)
void searching(void); //mainsys.c
5.查找UID返回結點指針(代碼重用)
pUser getuP_byuid(pUser puHead, int uid, pUser pMod); //admin_operate.c
6.重置所有信息
void sys_reset(void); //mainsys.c
7.輸入判斷函數
void operate_choice(int *num); //mainsys.c
Bug:
保存每期開獎信息有時不正確;
//admin_operate.c --> void write_winner_file(pLott plHead, int win_num);
項目總結
1.自上而下的設計模式,搭建整體框架,不必要想太細,不要過早優化;
2.寫代碼想清楚再動手,code review之後再進行編譯;
3.適度代碼重構(代碼複用度高),注意項目進度;
4.加深對一些知識的理解(List/IO/動態內存分配);
5.一個好的編輯器;
6.單鏈表的結點數據域/指針域要獨立,數據域作爲一個單獨的結構體嵌入到結點;
7.鏈表的插入操作視情況選擇插入到尾部還是頭結點之後,可以保留尾指針,一定要注意效率;
8.定義了一個指針pa,想在堆中爲pa分配內存,無需將pa傳入被調函數中,在被調函數中聲明
一個同類型的指針pb, malloc分配內存之後返回pb,用pa接收.
錯誤1.傳入pa再返回pb,pa的確指向了堆中分配的空間,但理解錯誤;
原因:編譯器爲pa創建副本_pa(棧中),表面上對pa進行操作其實是對_pa的操作,而這些
隨着被調函數結束,_pa消亡,pa沒有任何更改;關鍵是返回值pb在起作用.
錯誤2.傳入pa沒有返回值,pa並沒有指向分配的內存,後面調用pa可能會出錯;
原因:編譯器爲函數的每個參數創建副本,pa的副本爲_pa,_pa和pa指向同樣的地址,
如果函數體修改_pa指向的地址中的內容,pa指向的內容自然會相應的作出修改,但如果
在函數體內部爲參數指針動態分配內存,結果只是_pa指向的地址發生了變化,而pa指向的地址
中的內容沒有發生變化,沒有達到相應的目的.
9.如果指針pa指向一塊堆內存,如果要對指針pa進行下移(不是偏移)時,最好創建一個臨時指針
ptmp = pa;否則free時可能會出錯;
10.入參檢測(特別是指針),另外函數的返回值也要檢測,否則會有無法預知的錯誤;
11.函數封裝,人機交互放到函數體外部,傳入參數即可;
12.善於利用函數返回值;
By guangxyou 2013-8-8