2、整潔的代碼編程
程序1-1的三宗罪,分別是:代碼冗長、容易出錯和重用效果差。固然羅,如今網絡上面最流行找茬,別說三宗,n宗均可以找到。其實不管在網絡上仍是工做中,找茬都不是問題,問題是找到「茬」以後如何解決之。
【程序變】
應該說,程序1-1的「處理代碼」段遵循以下規則:
strncpy(域字符串變量, szBuf+域起始位置, 域長度);
域字符串變量[長度]=0;
其中,「字符串變量」和「域長度」在需求中已經肯定了,而「域起始位置」則是由手工計算而後寫入,如果某一步計算失誤,則會引發後續一系列的錯誤。既然如此,爲何不修改手工計算爲程序計算,好比設置一個整型變量s來「域起始位置」(就是代碼中的19、27、39等數字),初始時:
s=0;
每計算完一個域後,s向前移動到新的域起始位置,移動公式爲:
s=s+長度;
如此就能夠徹底避免因爲人工計算錯誤而形成的bug,代碼1-1中「處理代碼」部分變化以下:
strncpy(域字符串變量, szBuf + s, 域長度); //s表明了當前域在szBuf的起始位置
域字符串變量[域長度]=0;
s = s + 域長度; //更改後,s爲下一個域在szBuf中的起始位置
依照上述思想更改程序,「處理代碼」段以下:
- int s = 0; //記錄每一個數據域起始位置的變量
- /* 如下爲處理代碼 */
- strncpy(szAccno, szBuf+s, 19);
- szAccno[19]=0;
- s += 19;
- strncpy(szName, szBuf+s, 8);
- szName[8]=0;
- s += 8;
- strncpy(szAmt, szBuf+s, 12);
- szAmt[12]=0;
- s += 12;
- strncpy(szDate, szBuf+s, 8);
- szDate[8]=0;
- s += 8;
- strncpy(szLine, szBuf+s, 8);
- szLine[8]=0;
- s += 8;
- strncpy(szStatus, szBuf+s, 4);
- szStatus[4]=0;
- s += 4;
- strncpy(szBz, szBuf+s, 10);
- szBz[10]=0;
- s += 10;
代碼1-2
【編程浪子曰】
實現一個一樣的功能,程序的質量與代碼行的多少並沒有直接聯繫。
正如文學寫做時「有言則長,無言則短」通常,篇幅的長度不是決定若貝爾文學獎的因素,程序的質量也同樣。對比代碼1-1和代碼1-2,顯然前者的代碼行要比後者的少,質量卻不如後者,緣由有二:
其一,程序1-1更容易犯錯,因爲須要手工計算數據寫入代碼中,假若一步失誤則步步失誤。後者增長了一行代碼自動實現這個計算過程,不存在失誤的可能性。
其二,程序1-1更難於維護。假若數據域長度或者順序發生變化,則須要從新計算全部數據域的起始位置,而程序1-2中只須要更改代碼行「s+=域長度」中的域長度便可。
【程序再變】
再次回顧代碼1-2,「左看右看上看下看」仍是都以爲不順眼,總以爲不該該爲每個數據域單獨編寫代碼,這樣既帶來了工做量又容易出錯還不利於擴展,若是可以使用一個循環來完成,那就太好不過了。
影響循環的攔路虎之一就是每一個域長度沒法統一,不能寫入循環代碼中,難啊!
滄海橫流,方顯英雄本色!這個時間就須要數組出馬了。
【編程浪子曰】
把一堆毫無關聯的數據組合到一個數組中,就能夠經過數組名稱和下標以循環的方式來訪問了。
定義一個整型數組len描述每個數據域的長度,其中len[0]表明第一個域(帳號)的長度,len[1]表明第二個數據域姓名的長度,以此類推,則處理代碼字段能夠抽象爲:
//第i次循環——len[i]是當前處理數據域的長度
strncpy(域字符串變量, szBuf + s, len[i]);
域字符串變量[len[i]]=0;
s = s + len[i];
其中,長度數組len的定義以下:
int len[] = {19, 8, 12, 8, 8, 4, 10};
以上更改彷佛大功告成,不少紙上談兵者也是這麼認爲的,其實否則,由於雖然s和len[i]能夠出如今循環代碼中,可是每一個數據域的「域字符串變量」卻不同,最終代碼只能以以下的方式含恨收場:
- strncpy(szAccno, szBuf+s, len[0]);
- szAccno[len[0]]=0;
- s += len[0];
- strncpy(szName, szBuf+s, len[1]);
- szName[len[1]]=0;
- s += len[1];
思路到了這裏,算是卡住了,不少人可能打算就此收工,卻不知成功是無功而返之間也許只隔了辦毫米——聽說萊斯研究的電話時由於一顆螺絲少擰了半毫米,結果被貝爾捷足先登——是該想辦法完成這最後的半毫米。
在上個世紀的中國點子爲王,誰有一個好點子就能夠成功。但如今不行了,這裏有了一顆把數據域長度集合到一個數組中的點子,但貌似程序化簡仍然沒有成功。這是由於現今思想大解放,好點子太多了,一個好點子不必定可以成功——至少須要兩個。
回到程序,爲了解決「域字符串變量」不一致的問題,徹底能夠這樣:
【點子】
另外定義一個字符串數組varData[][20],其每一個元素表明了一個數據域,當處理第i+1個數據域時,只需將數據拷貝入varData [i]便可。
處理代碼字段能夠抽象爲:
- //第i次循環
- strncpy(varData[i], szBuf + s, len[i]);
- varData[i][len[i]]=0;
- s = s + len[i];
因而,程序能夠經過循環方式化簡以下:
- #include <stdio.h>
- #include <stdlib.h>
- #define LEN 7
- int main(int argc, char *argv[])
- {
- char szBuf[]="9559901010008888888木鴻飛 600.00 20110630063001230000測試一次 ";
- char varData[LEN][20];//記載全部數據域的數組
- int len[LEN] = {19, 8, 12, 8, 8, 4, 10};
- int s = 0, i;
- /* 如下爲處理代碼 */
- for (i=0; i<LEN; i++)
- {
- strncpy(varData[i], szBuf + s, len[i]);
- varData[i][len[i]]=0;
- s += len[i];
- }
- /* 如下爲打印代碼 */
- for (i=0; i<LEN; i++)
- {
- printf("第%d號域, 【%s】\n", i+1, varData[i]);
- }
- system("PAUSE");
- return 0;
- }
代碼1-3
【技巧】
其1、上述程序中使用了宏「#define LEN 7」,利用LEN來代替數字7,這種作法是爲了便於程序擴展。假設不使用宏LEN,則代碼中一定屢次出現數字7(本處爲4次),而一旦需求變動,數據域數量增長或是減小,就必須更改代碼中的每一次,一旦漏掉了某處,都將給程序代碼不可估量的損失。使用宏LEN後,只需一次更改便可。
其2、數字元素自己能夠做爲數組的下標,好比表達式varData[i][len[i]]中,數組元素len[i]成爲了二維數組varData的下標。
【疑問】
需求中要求將各個域數據存入szAccno、szName等數組中,而代碼1-3彷佛太過於大膽了,竟然私自更改需求,將之存儲與另外的字符串數組中,如此行爲,是可忍熟不可忍。
【編程浪子曰】
用戶的需求是能夠引導的。
不少時候,用戶並非特別清楚本身的需求,尤爲在某些細節方便。好比本處,開發者徹底能夠引導客戶,好比說:「數據域的存儲位置無所謂,只要可以拆分出來就能夠了。」
【程序又變】
可是,有的時候,有的客戶是很執着的,他認定的需求就是不能更改,此時需求再難也必須徹徹底底的完成。
再次回到程序的處理核心,以下:
strncpy(域字符串變量, szBuf + s, len[i]);
域字符串變量[len[i]]=0;
s = s + len[i];
其實,前面已經有了成功方案,就是將一羣徹底不關聯的數據域長度集合到一個整型數組中。既然能夠集合整數,固然也能夠集合「字符串變量」,要知道字符串變量本質上就是指針,那麼徹底能夠將之集合到一個指針數組之中。
【點子】
定義一個數組varP,其元素的類型是字符串指針(char *),其元素分別記載了各個存儲數據域的的存儲位置。
你們千萬不要一據說「指針數組」就以爲一個頭有兩個大,其實很簡單,就是一個數組,這數組的每一個元素都是一個指針,其定義方式以下:
char *varP[LEN];
假若還不明白,就看看它的賦值語句:
varP[0] = szAccno;
varP[1] = szName;
……
varP[6] = szBz;
固然,上述的賦值過程仍是過於複製,至少須要n條語句,實際上是能夠化簡的,以下所示:
char * varP[LEN] = {szAccno, szName, szAmt, szDate, szLine, szStatus, szBz};
有關指針數組的詳細介紹,等到本書「指針與數組」一章中會有詳細介紹,這裏仍是先看程序的改變結果,以下:
- #include <stdio.h>
- #include <stdlib.h>
- #define LEN 7
- int main(int argc, char *argv[])
- {
- char szBuf[]="9559901010008888888木鴻飛 600.00 20110630063001230000測試一次 ";
- char szAccno[20]; //表明"帳戶"
- char szName[9]; //表明"姓名"
- char szAmt[13]; //表明"交易金額"
- char szDate[9]; //表明"交易日期"
- char szLine[9]; //表明"交易流水號"
- char szStatus[5]; //表明"交易狀態"
- char szBz[11]; //表明"交易說明"
- char varData[LEN][20];//記載全部數據域的數組
- int len[LEN] = {19, 8, 12, 8, 8, 4, 10};
- char * varP[LEN] = {szAccno, szName, szAmt, szDate, szLine, szStatus, szBz};
- /* 如下爲處理代碼 */
- int s = 0, i;
- for (i=0; i<LEN; i++)
- {
- strncpy(varP[i], szBuf + s, len[i]);
- (varP[i])[len[i]]=0;
- s += len[i];
- }
- /* 如下爲打印代碼 */
- for (i=0; i<LEN; i++)
- {
- printf("第%d號域, 【%s】\n", i+1, varP[i]);
- }
- system("PAUSE");
- return 0;
- }
代碼1-3
【總結】
數組的妙用之一就在於其能化繁雜爲簡單,將一系列毫無聯繫的內容集合在一個數組中,就能夠經過循環的方式處理之,從而大大的簡化程序代碼。
做業3:
上述程序在設計好以後需求發生變動,報文格式變動以下:
字符串第1位~8位表明了「交易日期」; //位置提早
字符串第9位~20位表明了「交易流水號」; //位置提早,長度加長
字符串第21位~39位表明了「帳戶」;
字符串第40位~47位表明了「姓名」;
字符串第48位~63位表明了「交易金額」; //長度加長
字符串第64位~71位表明了「傳票號」; //新增域
字符串第72位~75位表明了「交易狀態」;
//取消了「備註」域。
字符串實例和變量狀況以下:
- char szBuf[]="201106300630123456789559901010008888888木鴻飛 600.00 999912340000";
- char szAccno[20]; //表明「帳戶」
- char szName[9]; //表明「姓名」
- char szAmt[17]; //表明「交易金額」
- char szDate[9]; //表明「交易日期」
- char szLine[13]; //表明「交易流水號」
- char szStatus[5]; //表明「交易狀態」
- char szBill[9]; //表明「傳票」
做業4:
上述程序在設計好以後需求發生變動,運行結果要求打印每號域的域說明,好比:
第1號域,帳號,【9559901010008888888】
第2號域,戶名,【木鴻飛 】
前一篇 目錄 後一篇
PS1:歡迎跟帖,寫下本身的做業心得。
PS2:徵求名稱
本書將講述數組相關的知識與應用,適用語言:C語言。
描述顯示:每次經過一個案例來講明。好比當前爲字符串報文解析程序,接下來立刻使用音樂演奏程序。
目前考慮的名稱有:
(1)數組達人成長之路。
(2)我愛數組
(3)別告訴我你懂數組
(4)數組玩轉趣味程序
你以爲那個名稱更加吸收眼球,或者你有什麼好的建議,歡迎跟帖。