這學期的作業中, 假設我們替一間 VCD/DVD/Game 及漫畫圖書的出租店設計一套管理系統, 我們希望這套管理系統可以記錄所有店內可供出租的媒體資料, 記錄所有客戶的個人靜態資料, 記錄所有媒體的租借資料、到期資料等等, 記錄所有客戶的帳款資料、預約資料等等動態資料。 我們希望這個管理系統有一個媒體資料登錄與刪除異動的界面, 希望在櫃台的地方有一個可以在客戶想要租借媒體時可以迅速登錄的界面, 希望有一個界面可以每個月整理客戶的帳單以便寄出。
在第一次的作業裡希望大家先複習一下 C 的語法和基本的資料結構的設計與運用, 希望大家能夠清楚單元測試的基本方法。 在上述的系統中想要把媒體的相關資料和客戶的相關資料記錄在程式裡的話, 程式中需要有適當的資料結構來存放, 對於這些資料的基本需求如下:
串列的每一個節點需要依序記錄的欄位內容如下:(以 VCD 為例)
程式需要提供一些文字選單界面, 讓操作的人可以更改串列中每一筆 VCD 的資料如下:
1) Insert Head 2) Append Tail 3) Insert After 4) Delete Head 5) Delete Tail 6) Delete Title 7) Calculate and Print CRC32 Check Sum 8) Delete All 9) Print Title 10) Print All 11) Quit 請輸入你的選項 (1-11) ?上面的這些功能實際上和串列的架構是密不可分的, 最主要是要讓大家的作業能夠很快地通過自動測試而設計的, 當然如果這些界面是要給真證的使用者來用的話, 必須把串列相關的結構隱藏起來, 只能提供類似 "加入" "刪除" "修改" "列印" "尋找" 等等功能。
基本測試資料如下:
1 ; first command, insert head title 1 artist 1 performer 1 64 1999 10 EMI 1 300 2 ; second command, append tail title 2 artist 2 performer 2 64 1999 10 EMI 1 300 3 ; third command, insert after title 1 title 3 artist 3 performer 3 64 1999 10 EMI 1 300 7 ; 7-th command, print check sum, should see [[6e94b237]] 4 ; fourth command, delete head 5 ; fifth command, delete tail 6 ; sixth command, delete title title 3 7 ; 7-th command, print check sum should see [[ffffffff]] 8 ; 8-th command, delete all record 9 ; 9-th command, print a title title 2 10 ; 10-th command, print all titles 11 : quit the program測試資料 程式執行後印出之 CRC32 checksum 為 f6d72415
這個作業中有不少的要求, 基本上都是和要求你設計一個大一點的軟體系統相關的, 請你一定要了解並且逐步完成。
#include <assert.h> ... assert(len == 10); assert(crc == 0x12345678L);
#define ETHERPOLY 0xedb88320L void updateCRC(unsigned long *crc32, unsigned char *buf, int len) { int i, j; unsigned char b; for (i=0; i<len; i++) { b = buf[i]; for (j=0; j<8; j++) { if ((*crc32 ^ b) & 1) *crc32 = (*crc32 >> 1) ^ ETHERPOLY; else *crc32 >>= 1; b >>= 1; } } }呼叫上面函式時請先準備一個 unsigned long 變數 crc32, 初始化為 0xFFFFFFFFL, 例如:
char title[80]; short int data; unsigned long crc32 = 0xFFFFFFFFL; strncpy(title, "A Beutiful Mind",80); updateCRC(&crc32, title, strlen(title)); data = 100; updateCRC(&crc32, (unsigned char *)&data, sizeof(short int)); printf("[[%lx]]", crc32);列印時請以十六進位輸出到螢幕,前後並加方括號如下:
這個作業有很多的規定, 希望大家一定要符合要求, 相對地很多同學也可能覺得好像沒有什麼可以發揮的地方, 會有這樣子狀況發生的原因, 最主要是因為物件導向的程式製作, 基本上是希望你把撰寫程式當成是工程領域中很單純一個模組的製作, 有很清楚的規格與程式功能的定義, 有很清楚的完成步驟與方法, 寫程式不能是很藝術的東西, 否則沒有辦法讓很多人一起合作來製作比較大的軟體系統, 製作好的軟體系統也沒有辦法持續地修改及維護下去。如果你實在很難耐住去製作特殊功能的衝動的話, 其實你還是有很多的選擇的, 比方說你可以製作排序的功能, 自己在程式裡加好所有的單元測試, 在交作業的時候特別地說明相關的設計... 不過請特別注意不管你加入什麼東西, 不能不滿足作業基本的要求, 否則自動的測試就沒有辦法通過了。 另外其實單元測試是很不容易做的, 你自己寫的一組函式該怎樣測試才能夠佐證它們是可靠的呢? 這裡每個人的想法會不一樣, 如果你抄別人的話也很容易看得出來。
short int x; scanf("%d", &x);是會產生記憶體存取錯誤的用法, 請小心
#ifdef NDEBUG release version of codes #else debug version of codes #endif編譯時指定 cl /DNDEBUG list.cpp 則可以產生 release 版本, 所有的 assert() 敘述會自動地跳過去, 如果用 cl list.cpp 編譯則可以產生 debug 版本。
char *fun1() { char *ptr, buf[100]; gets(buf); ptr = (char *)malloc(sizeof(buf)+1); ptr = buf; printf("1: %s\n", ptr); return ptr; } void fun2(char *ptr) { printf("3: %s\n", ptr); } void main() { char *ptr; ptr = fun1(); printf("2: %s\n", ptr); fun2(ptr); }你知道為什麼 printf("1 和 printf("2 都會對, 而 printf("3 會錯嗎? 請注意我的問題在於為什麼 printf("1 和 printf("2 會對?
... int x; ... x = 10; . . . assert(x==10);假設上面的程式從 x = 10 這個敘述開始 到 assert(x==10) 之間都沒有使用到 x 這個變數也都沒有任何地方間接或是直接 用到 x 這個變數的位址, 你覺得上面的程式一定是對的嗎?
邏輯上看起來應該不會錯, 但是如果中間的程式或是所呼叫的函式中有記憶體操作的錯誤 (指標的誤用, 未配置足夠的記憶體,字串的拷貝...) 的話, 那正確性就很值得懷疑了。
回
C++ 程式設計課程
首頁
製作日期: 3/10/2002
by 丁培毅 (Pei-yih Ting)
E-mail: pyting@cs.ntou.edu.tw
TEL: 02 24622192x6615
海洋大學
理工學院
資訊科學系