別告訴我你懂數組(2)

2、整潔的代碼編程

程序1-1的三宗罪,分別是:代碼冗長、容易出錯和重用效果差。固然羅,如今網絡上面最流行找茬,別說三宗,n宗均可以找到。其實不管在網絡上仍是工做中,找茬都不是問題,問題是找到「茬」以後如何解決之。 數組

【程序變】 網絡

應該說,程序1-1的「處理代碼」段遵循以下規則: ide

    strncpy(域字符串變量, szBuf+域起始位置, 域長度); 測試

    域字符串變量[長度]=0; spa

其中,「字符串變量」和「域長度」在需求中已經肯定了,而「域起始位置」則是由手工計算而後寫入,如果某一步計算失誤,則會引發後續一系列的錯誤。既然如此,爲何不修改手工計算爲程序計算,好比設置一個整型變量s來「域起始位置」(就是代碼中的192739等數字),初始時: 設計

s=0 指針

每計算完一個域後,s向前移動到新的域起始位置,移動公式爲: orm

s=s+長度; blog

如此就能夠徹底避免因爲人工計算錯誤而形成的bug,代碼1-1中「處理代碼」部分變化以下:

    strncpy(域字符串變量, szBuf + s, 域長度); //s表明了當前域在szBuf的起始位置

    域字符串變量[域長度]=0;                                                            

    s = s + 域長度;                                    //更改後,s爲下一個域在szBuf中的起始位置

依照上述思想更改程序,「處理代碼」段以下:

 

  
  
  
  
  1. int s = 0;            //記錄每一個數據域起始位置的變量   
  2. /* 如下爲處理代碼 */   
  3. strncpy(szAccno, szBuf+s, 19);  
  4. szAccno[19]=0;  
  5. s += 19;  
  6. strncpy(szName, szBuf+s, 8);  
  7. szName[8]=0;  
  8. s += 8;  
  9. strncpy(szAmt, szBuf+s, 12);  
  10. szAmt[12]=0;  
  11. s += 12;  
  12. strncpy(szDate, szBuf+s, 8);  
  13. szDate[8]=0;  
  14. s += 8;  
  15. strncpy(szLine, szBuf+s, 8);  
  16. szLine[8]=0;  
  17. s += 8;  
  18. strncpy(szStatus, szBuf+s, 4);  
  19. szStatus[4]=0;  
  20. s += 4;  
  21. strncpy(szBz, szBuf+s, 10);  
  22. szBz[10]=0;  
  23. 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};

以上更改彷佛大功告成,不少紙上談兵者也是這麼認爲的,其實否則,由於雖然slen[i]能夠出如今循環代碼中,可是每一個數據域的「域字符串變量」卻不同,最終代碼只能以以下的方式含恨收場:

 

  
  
  
  
  1. strncpy(szAccno, szBuf+s, len[0]);  
  2. szAccno[len[0]]=0;  
  3. s += len[0];  
  4. strncpy(szName, szBuf+s, len[1]);  
  5. szName[len[1]]=0;  
  6. s += len[1]; 

思路到了這裏,算是卡住了,不少人可能打算就此收工,卻不知成功是無功而返之間也許只隔了辦毫米——聽說萊斯研究的電話時由於一顆螺絲少擰了半毫米,結果被貝爾捷足先登——是該想辦法完成這最後的半毫米。

在上個世紀的中國點子爲王,誰有一個好點子就能夠成功。但如今不行了,這裏有了一顆把數據域長度集合到一個數組中的點子,但貌似程序化簡仍然沒有成功。這是由於現今思想大解放,好點子太多了,一個好點子不必定可以成功——至少須要兩個。

回到程序,爲了解決「域字符串變量」不一致的問題,徹底能夠這樣:

【點子】

另外定義一個字符串數組varData[][20],其每一個元素表明了一個數據域,當處理第i+1個數據域時,只需將數據拷貝入varData [i]便可。

處理代碼字段能夠抽象爲:

 

  
  
  
  
  1. //第i次循環  
  2.     strncpy(varData[i], szBuf + s, len[i]);     
  3.     varData[i][len[i]]=0;                           
  4.     s = s + len[i];    

 

因而,程序能夠經過循環方式化簡以下:

 

  
  
  
  
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.  
  4. #define LEN 7  
  5. int main(int argc, char *argv[])  
  6. {  
  7.     char szBuf[]="9559901010008888888木鴻飛  600.00      20110630063001230000測試一次  ";  
  8.     char varData[LEN][20];//記載全部數據域的數組   
  9.     int len[LEN] = {19, 8, 12, 8, 8, 4, 10};  
  10.     int s = 0, i;  
  11.  
  12.     /* 如下爲處理代碼 */   
  13.     for (i=0; i<LEN; i++)  
  14.     {  
  15.         strncpy(varData[i], szBuf + s, len[i]);  
  16.         varData[i][len[i]]=0;  
  17.         s += len[i];  
  18.     }      
  19.          /* 如下爲打印代碼 */ 
  20.     for (i=0; i<LEN; i++)  
  21.     {  
  22.         printf("第%d號域, 【%s】\n",  i+1, varData[i]);  
  23.     }      
  24.     system("PAUSE");    
  25.     return 0;  
  26. }  
  27.  

代碼1-3

【技巧】

其1、上述程序中使用了宏「#define LEN 7」,利用LEN來代替數字7,這種作法是爲了便於程序擴展。假設不使用宏LEN,則代碼中一定屢次出現數字7(本處爲4次),而一旦需求變動,數據域數量增長或是減小,就必須更改代碼中的每一次,一旦漏掉了某處,都將給程序代碼不可估量的損失。使用宏LEN後,只需一次更改便可。

其2、數字元素自己能夠做爲數組的下標,好比表達式varData[i][len[i]]中,數組元素len[i]成爲了二維數組varData的下標。

【疑問】

需求中要求將各個域數據存入szAccnoszName等數組中,而代碼13彷佛太過於大膽了,竟然私自更改需求,將之存儲與另外的字符串數組中,如此行爲,是可忍熟不可忍。

【編程浪子曰】

用戶的需求是能夠引導的。

不少時候,用戶並非特別清楚本身的需求,尤爲在某些細節方便。好比本處,開發者徹底能夠引導客戶,好比說:「數據域的存儲位置無所謂,只要可以拆分出來就能夠了。」

【程序又變】

可是,有的時候,有的客戶是很執着的,他認定的需求就是不能更改,此時需求再難也必須徹徹底底的完成。

再次回到程序的處理核心,以下:

    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};

有關指針數組的詳細介紹,等到本書「指針與數組」一章中會有詳細介紹,這裏仍是先看程序的改變結果,以下:

 

  
  
  
  
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.  
  4. #define LEN 7  
  5. int main(int argc, char *argv[])  
  6. {  
  7.     char szBuf[]="9559901010008888888木鴻飛  600.00      20110630063001230000測試一次  ";  
  8.     char szAccno[20]; //表明"帳戶"  
  9.     char szName[9];      //表明"姓名"  
  10.     char szAmt[13];      //表明"交易金額"  
  11.     char szDate[9];      //表明"交易日期"  
  12.     char szLine[9];      //表明"交易流水號"  
  13.     char szStatus[5]; //表明"交易狀態"  
  14.     char szBz[11];       //表明"交易說明"  
  15.     char varData[LEN][20];//記載全部數據域的數組   
  16.     int len[LEN] = {19, 8, 12, 8, 8, 4, 10};  
  17.     char * varP[LEN] = {szAccno, szName, szAmt, szDate, szLine, szStatus, szBz};  
  18.     /* 如下爲處理代碼 */   
  19.     int s = 0, i;  
  20.     for (i=0; i<LEN; i++)  
  21.     {  
  22.         strncpy(varP[i], szBuf + s, len[i]);  
  23.         (varP[i])[len[i]]=0;  
  24.         s += len[i];  
  25.     }      
  26.     /* 如下爲打印代碼 */ 
  27.     for (i=0; i<LEN; i++)  
  28.     {  
  29.         printf("第%d號域, 【%s】\n",  i+1, varP[i]);  
  30.     }      
  31.  
  32.     system("PAUSE");    
  33.     return 0;  
  34. }  
  35.  

 

代碼1-3

【總結】

數組的妙用之一就在於其能化繁雜爲簡單,將一系列毫無聯繫的內容集合在一個數組中,就能夠經過循環的方式處理之,從而大大的簡化程序代碼。

做業3

上述程序在設計好以後需求發生變動,報文格式變動以下:

字符串第1位~8位表明了「交易日期」;        //位置提早

字符串第9位~20位表明了「交易流水號」;     //位置提早,長度加長

字符串第21位~39位表明了「帳戶」;

字符串第40位~47位表明了「姓名」;

字符串第48位~63位表明了「交易金額」;      //長度加長

字符串第64位~71位表明了「傳票號       //新增域

字符串第72位~75位表明了「交易狀態」;

//取消了「備註」域。

字符串實例和變量狀況以下:

 

  
  
  
  
  1. char szBuf[]="201106300630123456789559901010008888888木鴻飛  600.00          999912340000";  
  2. char szAccno[20];          //表明「帳戶」  
  3. char szName[9];          //表明「姓名」  
  4. char szAmt[17];           //表明「交易金額」  
  5. char szDate[9];            //表明「交易日期」  
  6. char szLine[13];          //表明「交易流水號」  
  7. char szStatus[5];         //表明「交易狀態」  
  8. char szBill[9];           //表明「傳票」 

做業4

上述程序在設計好以後需求發生變動,運行結果要求打印每號域的域說明,好比:

1號域,帳號,【9559901010008888888

2號域,戶名,【木鴻飛  

 前一篇   目錄   後一篇

PS1:歡迎跟帖,寫下本身的做業心得。

PS2:徵求名稱

本書將講述數組相關的知識與應用,適用語言:C語言。

描述顯示:每次經過一個案例來講明。好比當前爲字符串報文解析程序,接下來立刻使用音樂演奏程序。

目前考慮的名稱有:

(1)數組達人成長之路。

(2)我愛數組

(3)別告訴我你懂數組

(4)數組玩轉趣味程序

你以爲那個名稱更加吸收眼球,或者你有什麼好的建議,歡迎跟帖。

相關文章
相關標籤/搜索