程式設計修養(五) (轉)

amyz發表於2007-08-14
程式設計修養(五) (轉)[@more@]

16、把相同或近乎相同的程式碼形成和宏
—————————————————————

有人說,最好的員,就是最喜歡“偷懶”的程式,其中不無道理。

如果你有一些程式的程式碼片段很相似,或直接就是一樣的,請把他們放在一個函式中。而如果這段程式碼不多,而且會被經常使用,你還想避免函式的開銷,那麼就把他寫成宏吧。

千萬不要讓同一份程式碼或是功能相似的程式碼在多個地方存在,不然如果功能一變,你就要修改好幾處地方,這種會給維護帶來巨大的麻煩,所以,做到“一改百改”,還是要形成函式或是宏。

 

17、中的括號
—————————

如果一個比較複雜的表示式中,你並不是很清楚各個運算子的憂先級,即使是你很清楚優先順序,也請加上括號,不然,別人或是自己下一次讀程式時,一不小心就看走眼理解錯了,為了避免這種“誤解”,還有讓自己的程式更為清淅,還是加上括號吧。

比如,對一個結構的成員取地址:

  GetUserAge( &( UserInfo->age ) );

雖然,&UserInfo->age中,->運算子的優先順序最高,但加上一個括號,會讓人一眼就看明白你的程式碼是什麼意思。

再比如,一個很長的條件判斷:

if ( ( ch[0] >= '0' || ch[0] <= '9' ) &&
  ( ch[1] >= 'a' || ch[1] <= 'z' ) &&
  ( ch[2] >= 'A' || ch[2] <= 'Z' )  )
 
括號,再加上空格和換行,你的程式碼是不是很容易讀懂了? 


18、函式引數中的const
———————————

對於一些函式中的指標引數,如果在函式中只讀,請將其用const修飾,這樣,別人一讀到你的函式介面時,就會知道你的意圖是這個引數是[in],如果沒有const時,參數列示[in/out],注意函式介面中的const使用,利於程式的維護和避免犯一些錯誤。

雖然,const修飾的指標,如:const char* p,在C中一點用也沒有,因為不管你的宣告是不是const,指標的內容照樣能改,因為會強制轉換,但是加上這樣一個說明,有利於程式的閱讀和編譯。因為在C中,修改一個const指標所指向的時,會報一個Warning。這會引起程式設計師的注意。

C++中對const定義的就很嚴格了,所以C++中要多多的使用const,const的成員函式,const的變數,這樣會對讓你的程式碼和你的程式更加完整和易讀。(關於C++的const我就不多說了)

 

19、函式的引數個數(多了請用結構)
—————————————————

函式的引數個數最好不要太多,一般來說6個左右就可以了,眾多的函式引數會讓讀程式碼的人一眼看上去就很頭昏,而且也不利於維護。如果引數眾多,還請使用結構來傳遞引數。這樣做有利於資料的封裝和程式的簡潔性。

也利於使用函式的人,因為如果你的函式個數很多,比如12個,呼叫者很容易搞錯引數的順序和個數,而使用結構struct來傳遞引數,就可以不管引數的順序。

而且,函式很容易被修改,如果需要給函式增加引數,不需要更改函式介面,只需更改結構體和函式內部處理,而對於呼叫函式的程式來說,這個動作是透明的。

 


20、函式的返回型別,不要省略
——————————————

我看到很多程式寫函式時,在函式的返回型別方面不太注意。如果一個函式沒有返回值,也請在函式前面加上void的修飾。而有的程式設計師偷懶,在返回int的函式則什麼不修飾(因為如果不修飾,則預設返回int),這種習慣很不好,還是為了原始碼的易讀性,加上int吧。

所以函式的返回值型別,請不要省略。

另外,對於void的函式,我們往往會忘了return,由於某些C/C++的編譯器比較敏感,會報一些警告,所以即使是void的函式,我們在內部最好也要加上return的語句,這有助於程式碼的編譯。

 


21、goto語句的使用
—————————

N年前,開發的一代宗師——迪傑斯特拉(Dijkstra)說過:“goto statment is harmful !!”,並建議取消goto語句。因為goto語句不利於程式程式碼的維護性。

這裡我也強烈建議不要使用goto語句,除非下面的這種情況:


  #define FREE(p) if(p) {
  free(p);
  p = NULL;
  }

  main()
  {
  char *fname=NULL, *lname=NULL, *mname=NULL;

  fname = ( char* ) calloc ( 20, sizeof(char) );
  if ( fname == NULL ){
  goto ErrHandle;
  }

  lname = ( char* ) calloc ( 20, sizeof(char) );
  if ( lname == NULL ){
  goto ErrHandle;
  }

  mname = ( char* ) calloc ( 20, sizeof(char) );
  if ( mname == NULL ){
  goto ErrHandle;
  }
 
  ......
 
 
  ErrHandle:
  FREE(fname);
  FREE(lname);
  FREE(mname);
  ReportError(ERR_NO_MEMOEY);
  }

也只有在這種情況下,goto語句會讓你的程式更易讀,更容易維護。(在用嵌C來對設定遊標操作時,或是對資料庫建立連結時,也會遇到這種結構)

 


22、宏的使用
——————

很多程式設計師不知道C中的“宏”到底是什麼意思?特別是當宏有引數的時候,經常把宏和函式混淆。我想在這裡我還是先講講“宏”,宏只是一種定義,他定義了一個語句塊,當程式編譯時,編譯器首先要一個“替換”源程式的動作,把宏引用的地方替換成宏定義的語句塊,就像文字替換一樣。這個動作術語叫“宏的展開”

使用宏是比較“危險”的,因為你不知道宏展開後會是什麼一個樣子。例如下面這個宏:

  #define  MAX(a, b)  a>b?a:b

當我們這樣使用宏時,沒有什麼問題: MAX( num1, num2 ); 因為宏展開後變成 num1>num2?num1:num2;。 但是,如果是這樣呼叫的,MAX( 17+32, 25+21 ); 呢,編譯時出現錯誤,原因是,宏展開後變成:17+32>25+21?17+32:25+21,哇,這是什麼啊?

所以,宏在使用時,引數一定要加上括號,上述的那個例子改成如下所示就能解決問題了。

  #define  MAX( (a), (b) )  (a)>(b)?(a):(b)
 
即使是這樣,也不這個宏也還是有,因為如果我這樣呼叫 MAX(i++, j++); , 經過這個宏以後,i和j都被累加了兩次,這絕不是我們想要的。
 
所以,在宏的使用上還是要謹慎考慮,因為宏展開是的結果是很難讓人預料的。而且雖然,宏的執行很快(因為沒有函式呼叫的開銷),但宏會讓澎漲,使目標檔案尺寸變大,(如:一個50行的宏,程式中有1000個地方用到,宏展開後會很不得了),相反不能讓程式執行得更快(因為執行檔案變大,執行時換頁頻繁)。

因此,在決定是用函式,還是用宏時得要小心。

/Develop/read_article.?id=18272"> 

(版權所有,轉載時請註明出處和作者資訊)


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-956445/,如需轉載,請註明出處,否則將追究法律責任。

相關文章