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

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

11、出錯資訊的處理
—————————
你會處理出錯資訊嗎?哦,它並不是簡單的輸出。看下面的示例:

  if ( p == NULL ){
  printf ( "ERR: The pointer is NULL " );
  }
 
告別學生時代的吧。這種程式設計很不利於維護和管理,出錯資訊或是提示資訊,應該統一處理,而不是像上面這樣,寫成一個“硬編碼”。第10條對這方面的處理做了一部分說明。如果要管理錯誤資訊,那就要有以下的處理:

  /* 宣告出錯程式碼 */
  #define  ERR_NO_ERROR  0  /* No error */
  #define  ERR_OPEN_FILE  1  /* Open file error */
  #define  ERR_SEND_MESG  2 /* sending a message error  */
  #define  ERR_BAD_ARGS  3 /* Bad arguments */
  #define  ERR_MEM_NONE  4 /* Memeroy is not enough  */
  #define  ERR_SERV_DOWN  5 /* Service down try later  */
  #define  ERR_UNKNOW_INFO 6 /* Unknow information  */
  #define  ERR_SOCKET_ERR  7 /* Socket operation failed  */
  #define  ERR_PESSION  8  /* Permission denied  */
  #define  ERR_BAD_FORMAT  9  /* Bad configuration file   */
  #define  ERR_TIME_OUT  10  /* Communication time out  */
 
  /* 宣告出錯資訊 */
  char* errmsg[] = {
  /* 0 */  "No error", 
  /* 1 */  "Open file error", 
  /* 2 */  "Failed in sending/receiving a message", 
  /* 3 */  "Bad arguments", 
  /* 4 */  "Memeroy is not enough",
  /* 5 */  "Service is down; try later",
  /* 6 */  "Unknow information",
  /* 7 */  "A socket operation has failed",
  /* 8 */  "Permission denied",
  /* 9 */  "Bad configuration file format", 
  /* 10 */  "Communication time out",
  };
 
  /* 宣告錯誤程式碼全域性變數 */
  long errno = 0;
 
  /* 列印出錯資訊 */
  void perror( char* info)
  {
  if ( info ){
  printf("%s: %s ", info, errmsg[errno] );
  return;
  }
 
  printf("Error: %s ", errmsg[errno] );
  }

這個基本上是ANSI的錯誤處理實現細節了,於是當你中有錯誤時你就可以這樣處理:

  bool CheckPermission( char* userName )
  {
  if ( strcpy(userName, "") != 0 ){
  errno = ERR_PERMISSION_DENIED;
  return (FALSE);
  }
 
  ...
  }
 
  main()
  {
  ...
  if (! CheckPermission( username ) ){
  perror("main()");
  }
  ...
  }
 
一個即有共性,也有個性的錯誤資訊處理,這樣做有利同種錯誤出一樣的資訊,統一介面,而不會因為開啟失敗,A程式設計師出一個資訊,B程式設計師又出一個資訊。而且這樣做,非常容易維護。程式碼也易讀。

當然,物極必反,也沒有必要把所有的輸出都放到errmsg中,抽取比較重要的出錯資訊或是提示資訊是其關鍵,但即使這樣,這也包括了大多數的資訊。

 


12、常用函式和迴圈語句中的被計算量
—————————————————
看一下下面這個例子:

  for( i=0; i<1000; i++ ){
  GetLocalHostName( hostname );
  ...
  }
 
GetLocalHostName的意思是取得當前名,在迴圈體中,它會被1000次啊。這是多麼的沒有的事啊。應該把這個函式拿到迴圈體外,這樣只呼叫一次,效率得到了很大的提高。雖然,我們的會進行,會把迴圈體內的不變的東西拿到迴圈外面,但是,你相信所有編譯器會知道哪些是不變的嗎?我覺得編譯器不可靠。最好還是自己動手吧。

同樣,對於常用函式中的不變數,如:

GetLocalHostName(char* name)
{
  char funcName[] = "GetLocalHostName";
 
  sys_log( "%s begin......", funcName );
  ...
  sys_log( "%s end......", funcName );
}

如果這是一個經常呼叫的函式,每次呼叫時都要對funcName進行分配,這個開銷很大啊。把這個變數宣告成static吧,當函式再次被呼叫時,就會省去了分配記憶體的開銷,效率也很好。
 

 

13、函式名和變數名的命名
————————————
我看到許多程式對變數名和函式名的取名很草率,特別是變數名,什麼a,b,c,aa,bb,cc,還有什麼flag1,flag2, cnt1, cnt2,這同樣是一種沒有“修養”的行為。即便加上好的註釋。好的變數名或是函式名,我認為應該有以下的規則:
 
  1) 直觀並且可以拼讀,可望文知意,不必“解碼”。
  2) 名字的長度應該即要最短的長度,也要能最大限度的表達其含義。
  3) 不要全部大寫,也不要全部小寫,應該大小寫都有,如:GetLocalHostName 或是 UserAccount。
  4) 可以簡寫,但簡寫得要讓人明白,如:ErrorCode -> ErrCode,  ServerListener -> ServLisner,UserAccount -> UsrAcct 等。
  5) 為了避免全域性函式和變數名字衝突,可以加上一些字首,一般以模組簡稱做為字首。
  6) 全域性變數統一加一個字首或是字尾,讓人一看到這個變數就知道是全域性的。
  7) 用匈牙利命名法命名函式引數,區域性變數。但還是要堅持“望文生意”的原則。
  8) 與標準庫(如:STL)或開發庫(如:MFC)的命名風格保持一致。
 


 
14、函式的傳值和傳指標
————————————
向函式傳引數時,一般而言,傳入非const的指標時,就表示,在函式中要修改這個指標把指記憶體中的資料。如果是傳值,那麼無論在函式內部怎麼修改這個值,也影響不到傳過來的值,因為傳值是隻記憶體複製。

什麼?你說這個特性你明白了,好吧,讓我們看看下面的這個例程:

void
GetVersion(char* pStr)
{
  pStr = malloc(10);
  strcpy ( pStr, "2.0" );
}

main()
{
  char* ver = NULL;
  GetVersion ( ver );
  ...
  ...
  free ( ver );
}

我保證,類似這樣的問題是一個新手最容易犯的錯誤。程式中妄圖透過函式GetVersion給指標ver分配空間,但這種方法根本沒有什麼作用,原因就是——這是傳值,不是傳指標。你或許會和我爭論,我分明傳的時指標啊?再仔細看看,其實,你傳的是指標其實是在傳值。

 

15、修改別人程式的修養
———————————

當你維護別人的程式時,請不要非常主觀臆斷的把已有的程式刪除或是修改。我經常看到有的程式設計師直接在別人的程式上修改或是語句。修改別人的程式時,請不要刪除別人的程式,如果你覺得別人的程式有所不妥,請註釋掉,然後新增自己的處理程式,必竟,你不可能100%的知道別人的意圖,所以為了可以恢復,請不依賴於或是Safe這種版本控制,還是要在原始碼上給別人看到你修改程式的意圖和步驟。這是程式維護時,一個有修養的程式設計師所應該做的。

如下所示,這就是一種比較好的修改方法:

  /*
  * ----- commented by haoel /04/12 ------
  *
  *  char* p = ( char* ) malloc( 10 );
  *  memset( p, 0, 10 );
  */
 
  /* ------ Added by haoel  2003/04/12 ----- */
  char* p = ( char* )calloc( 10, sizeof char );
  /* ---------------------------------------- */
  ...

當然,這種方法是在軟體維護時使用的,這樣的方法,可以讓再維護的人很容易知道以前的程式碼更改的動作和意圖,而且這也是對原作者的一種尊敬。

以“註釋 — 新增”方式修改別人的程式,要好於直接刪除別人的程式。

/Develop/read_article.?id=18271"> 

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


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

相關文章