日韩床上生活一级视频|能看毛片的操逼网站|色悠悠网站在线观看视频|国产免费观看A淫色免费|国产av久久久久久久|免费A级视频美女网站黄|国产毛片av日韩小黄片|热久久免费国产视频|中文字幕无码色色|成人在线视频99久久久

機(jī)電之家資源網(wǎng)
單片機(jī)首頁|單片機(jī)基礎(chǔ)|單片機(jī)應(yīng)用|單片機(jī)開發(fā)|單片機(jī)文案|軟件資料下載|音響制作|電路圖下載 |嵌入式開發(fā)
培訓(xùn)信息
贊助商
編程修養(yǎng)
編程修養(yǎng)
 更新時(shí)間:2008-8-17 16:42:55  點(diǎn)擊數(shù):30
【字體: 字體顏色

這個(gè)說法不無道理,但是一般而言,對(duì)于一個(gè)比較大的系統(tǒng),errmsg是比較大的,所以產(chǎn)生副本導(dǎo)致執(zhí)行文件尺寸變大,不僅增加了系統(tǒng)裝載時(shí)間,也會(huì)讓一個(gè)程序在內(nèi)存中占更多的頁面。而對(duì)于errmsg這樣數(shù)據(jù),一般來說,在系統(tǒng)運(yùn)行時(shí)不會(huì)經(jīng)常用到,所以還是產(chǎn)生的內(nèi)存換頁也就不算頻繁。權(quán)衡之下,還是只有一份errmsg的效率高。即便是像logmsg這樣頻繁使用的的數(shù)據(jù),操作系統(tǒng)的內(nèi)存調(diào)度算法會(huì)讓這樣的頻繁使用的頁面常駐于內(nèi)存,所以也就不會(huì)出現(xiàn)內(nèi)存換頁問題了

11、出錯(cuò)信息的處理
---------
你會(huì)處理出錯(cuò)信息嗎?哦,它并不是簡單的輸出?聪旅娴氖纠
  if ( p == NULL ){
    printf ( "ERR: The pointer is NULL\n" );
  }
  
告別學(xué)生時(shí)代的編程吧。這種編程很不利于維護(hù)和管理,出錯(cuò)信息或是提示信息,應(yīng)該統(tǒng)一處理,而不是像上面這樣,寫成一個(gè)"硬編碼"。第10條對(duì)這方面的處理做了一部分說明。如果要管理錯(cuò)誤信息,那就要有以下的處理:
  /* 聲明出錯(cuò)代碼 */
  #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_PERMISSION 8 /* Permission denied    */
  #define   ERR_BAD_FORMAT 9 /* Bad configuration file  */
  #define   ERR_TIME_OUT  10 /* Communication time out  */
  
  /* 聲明出錯(cuò)信息 */
  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",
  };
               
  /* 聲明錯(cuò)誤代碼全局變量 */
  long errno = 0;
  
  /* 打印出錯(cuò)信息函數(shù) */
  void perror( char* info)
  {
    if ( info ){
      printf("%s: %s\n", info, errmsg[errno] );
      return;
    }
    
    printf("Error: %s\n", errmsg[errno] );
  }
這個(gè)基本上是ANSI的錯(cuò)誤處理實(shí)現(xiàn)細(xì)節(jié)了,于是當(dāng)你程序中有錯(cuò)誤時(shí)你就可以這樣處理:
  bool CheckPermission( char* userName )
  {
    if ( strcpy(userName, "root") != 0 ){
      errno = ERR_PERMISSION_DENIED;
      return (FALSE);
    }
    
    ...
  }
  
  main()
  {
    ...
    if (! CheckPermission( username ) ){
      perror("main()");
    }
    ...
  }
               
一個(gè)即有共性,也有個(gè)性的錯(cuò)誤信息處理,這樣做有利同種錯(cuò)誤出一樣的信息,統(tǒng)一用戶界面,而不會(huì)因?yàn)槲募蜷_失敗,A程序員出一個(gè)信息,B程序員又出一個(gè)信息。而且這樣做,非常容易維護(hù)。代碼也易讀。
當(dāng)然,物極必反,也沒有必要把所有的輸出都放到errmsg中,抽取比較重要的出錯(cuò)信息或是提示信息是其關(guān)鍵,但即使這樣,這也包括了大多數(shù)的信息。 12、常用函數(shù)和循環(huán)語句中的被計(jì)算量
-----------------
看一下下面這個(gè)例子:
  for( i=0; i<1000; i++ ){
    GetLocalHostName( hostname );
    ...
  }
  
GetLocalHostName的意思是取得當(dāng)前計(jì)算機(jī)名,在循環(huán)體中,它會(huì)被調(diào)用1000次啊。這是多么的沒有效率的事啊。應(yīng)該把這個(gè)函數(shù)拿到循環(huán)體外,這樣只調(diào)用一次,效率得到了很大的提高。雖然,我們的編譯器會(huì)進(jìn)行優(yōu)化,會(huì)把循環(huán)體內(nèi)的不變的東西拿到循環(huán)外面,但是,你相信所有編譯器會(huì)知道哪些是不變的嗎?我覺得編譯器不可靠。最好還是自己動(dòng)手吧。
同樣,對(duì)于常用函數(shù)中的不變量,如:
GetLocalHostName(char* name)
{
  char funcName[] = "GetLocalHostName";
  
  sys_log( "%s begin......", funcName );
  ...
  sys_log( "%s end......", funcName );
}
如果這是一個(gè)經(jīng)常調(diào)用的函數(shù),每次調(diào)用時(shí)都要對(duì)funcName進(jìn)行分配內(nèi)存,這個(gè)開銷很大啊。把這個(gè)變量聲明成static吧,當(dāng)函數(shù)再次被調(diào)用時(shí),就會(huì)省去了分配內(nèi)存的開銷,執(zhí)行效率也很好。
  
13、函數(shù)名和變量名的命名
------------
我看到許多程序?qū)ψ兞棵秃瘮?shù)名的取名很草率,特別是變量名,什么a,b,c,aa,bb,cc,還有什么flag1,flag2, cnt1, cnt2,這同樣是一種沒有"修養(yǎng)"的行為。即便加上好的注釋。好的變量名或是函數(shù)名,我認(rèn)為應(yīng)該有以下的規(guī)則:
  
  1) 直觀并且可以拼讀,可望文知意,不必"解碼"。
  2) 名字的長度應(yīng)該即要最短的長度,也要能最大限度的表達(dá)其含義。
  3) 不要全部大寫,也不要全部小寫,應(yīng)該大小寫都有,如:GetLocalHostName 或是 UserAccount。
  4) 可以簡寫,但簡寫得要讓人明白,如:ErrorCode -> ErrCode, ServerListener -> ServLisner,UserAccount -> UsrAcct 等。
  5) 為了避免全局函數(shù)和變量名字沖突,可以加上一些前綴,一般以模塊簡稱做為前綴。
  6) 全局變量統(tǒng)一加一個(gè)前綴或是后綴,讓人一看到這個(gè)變量就知道是全局的。
  7) 用匈牙利命名法命名函數(shù)參數(shù),局部變量。但還是要堅(jiān)持"望文生意"的原則。
  8) 與標(biāo)準(zhǔn)庫(如:STL)或開發(fā)庫(如:MFC)的命名風(fēng)格保持一致。
  
  
14、函數(shù)的傳值和傳指針
------------
向函數(shù)傳參數(shù)時(shí),一般而言,傳入非const的指針時(shí),就表示,在函數(shù)中要修改這個(gè)指針把指內(nèi)存中的數(shù)據(jù)。如果是傳值,那么無論在函數(shù)內(nèi)部怎么修改這個(gè)值,也影響不到傳過來的值,因?yàn)閭髦凳侵粌?nèi)存拷貝。
什么?你說這個(gè)特性你明白了,好吧,讓我們看看下面的這個(gè)例程:
void
GetVersion(char* pStr)
{
  pStr = malloc(10);
  strcpy ( pStr, "2.0" );
}
main()
{
  char* ver = NULL;
  GetVersion ( ver );
  ...
  ...
  free ( ver );
}
我保證,類似這樣的問題是一個(gè)新手最容易犯的錯(cuò)誤。程序中妄圖通過函數(shù)GetVersion給指針ver分配空間,但這種方法根本沒有什么作用,原因就是--這是傳值,不是傳指針。你或許會(huì)和我爭論,我分明傳的時(shí)指針?再仔細(xì)看看,其實(shí),你傳的是指針其實(shí)是在傳值。

15、修改別人程序的修養(yǎng)
-----------
當(dāng)你維護(hù)別人的程序時(shí),請(qǐng)不要非常主觀臆斷的把已有的程序刪除或是修改。我經(jīng)常看到有的程序員直接在別人的程序上修改表達(dá)式或是語句。修改別人的程序時(shí),請(qǐng)不要?jiǎng)h除別人的程序,如果你覺得別人的程序有所不妥,請(qǐng)注釋掉,然后添加自己的處理程序,必竟,你不可能100%的知道別人的意圖,所以為了可以恢復(fù),請(qǐng)不依賴于CVS或是SourceSafe這種版本控制軟件,還是要在源碼上給別人看到你修改程序的意圖和步驟。這是程序維護(hù)時(shí),一個(gè)有修養(yǎng)的程序員所應(yīng)該做的。
如下所示,這就是一種比較好的修改方法:
  /*
   * ----- commented by haoel 2003/04/12 ------
   *
   *  char* p = ( char* ) malloc( 10 );
   *  memset( p, 0, 10 );
   */
  
  /* ------ Added by haoel  2003/04/12 ----- */
   char* p = ( char* )calloc( 10, sizeof char );
  /* ---------------------------------------- */
  ...
當(dāng)然,這種方法是在軟件維護(hù)時(shí)使用的,這樣的方法,可以讓再維護(hù)的人很容易知道以前的代碼更改的動(dòng)作和意圖,而且這也是對(duì)原作者的一種尊敬。
以"注釋 - 添加"方式修改別人的程序,要好于直接刪除別人的程序。

16、把相同或近乎相同的代碼形成函數(shù)和宏
---------------------
有人說,最好的程序員,就是最喜歡"偷懶"的程序,其中不無道理。
如果你有一些程序的代碼片段很相似,或直接就是一樣的,請(qǐng)把他們放在一個(gè)函數(shù)中。而如果這段代碼不多,而且會(huì)被經(jīng)常使用,你還想避免函數(shù)調(diào)用的開銷,那么就把他寫成宏吧。
千萬不要讓同一份代碼或是功能相似的代碼在多個(gè)地方存在,不然如果功能一變,你就要修改好幾處地方,這種會(huì)給維護(hù)帶來巨大的麻煩,所以,做到"一改百改",還是要形成函數(shù)或是宏。
17、表達(dá)式中的括號(hào)
---------
如果一個(gè)比較復(fù)雜的表達(dá)式中,你并不是很清楚各個(gè)操作符的憂先級(jí),即使是你很清楚優(yōu)先級(jí),也請(qǐng)加上括號(hào),不然,別人或是自己下一次讀程序時(shí),一不小心就看走眼理解錯(cuò)了,為了避免這種"誤解",還有讓自己的程序更為清淅,還是加上括號(hào)吧。
比如,對(duì)一個(gè)結(jié)構(gòu)的成員取地址:
  GetUserAge( &( UserInfo->age ) );
雖然,&UserInfo->age中,->操作符的優(yōu)先級(jí)最高,但加上一個(gè)括號(hào),會(huì)讓人一眼就看明白你的代碼是什么意思。
再比如,一個(gè)很長的條件判斷:
if ( ( ch[0] >= '0' ch[0] <= '9' ) &&
   ( ch[1] >= 'a' ch[1] <= 'z' ) &&
   ( ch[2] >= 'A' ch[2] <= 'Z' )  )
  
括號(hào),再加上空格和換行,你的代碼是不是很容易讀懂了?  

18、函數(shù)參數(shù)中的const
-----------
對(duì)于一些函數(shù)中的指針參數(shù),如果在函數(shù)中只讀,請(qǐng)將其用const修飾,這樣,別人一讀到你的函數(shù)接口時(shí),就會(huì)知道你的意圖是這個(gè)參數(shù)是[in],如果沒有const時(shí),參數(shù)表示[in/out],注意函數(shù)接口中的const使用,利于程序的維護(hù)和避免犯一些錯(cuò)誤。
雖然,const修飾的指針,如:const char* p,在C中一點(diǎn)用也沒有,因?yàn)椴还苣愕穆暶魇遣皇莄onst,指針的內(nèi)容照樣能改,因?yàn)榫幾g器會(huì)強(qiáng)制轉(zhuǎn)換,但是加上這樣一個(gè)說明,有利于程序的閱讀和編譯。因?yàn)樵贑中,修改一個(gè)const指針?biāo)赶虻膬?nèi)存時(shí),會(huì)報(bào)一個(gè)Warning。這會(huì)引起程序員的注意。
C++中對(duì)const定義的就很嚴(yán)格了,所以C++中要多多的使用const,const的成員函數(shù),const的變量,這樣會(huì)對(duì)讓你的代碼和你的程序更加完整和易讀。(關(guān)于C++的const我就不多說了)

19、函數(shù)的參數(shù)個(gè)數(shù)(多了請(qǐng)用結(jié)構(gòu))
-----------------
函數(shù)的參數(shù)個(gè)數(shù)最好不要太多,一般來說6個(gè)左右就可以了,眾多的函數(shù)參數(shù)會(huì)讓讀代碼的人一眼看上去就很頭昏,而且也不利于維護(hù)。如果參數(shù)眾多,還請(qǐng)使用結(jié)構(gòu)來傳遞參數(shù)。這樣做有利于數(shù)據(jù)的封裝和程序的簡潔性。
也利于使用函數(shù)的人,因?yàn)槿绻愕暮瘮?shù)個(gè)數(shù)很多,比如12個(gè),調(diào)用者很容易搞錯(cuò)參數(shù)的順序和個(gè)數(shù),而使用結(jié)構(gòu)struct來傳遞參數(shù),就可以不管參數(shù)的順序。
而且,函數(shù)很容易被修改,如果需要給函數(shù)增加參數(shù),不需要更改函數(shù)接口,只需更改結(jié)構(gòu)體和函數(shù)內(nèi)部處理,而對(duì)于調(diào)用函數(shù)的程序來說,這個(gè)動(dòng)作是透明的。


20、函數(shù)的返回類型,不要省略
--------------
我看到很多程序?qū)懞瘮?shù)時(shí),在函數(shù)的返回類型方面不太注意。如果一個(gè)函數(shù)沒有返回值,也請(qǐng)?jiān)诤瘮?shù)前面加上void的修飾。而有的程序員偷懶,在返回int的函數(shù)則什么不修飾(因?yàn)槿绻恍揎?則默認(rèn)返回int),這種習(xí)慣很不好,還是為了原代碼的易讀性,加上int吧。
所以函數(shù)的返回值類型,請(qǐng)不要省略。
另外,對(duì)于void的函數(shù),我們往往會(huì)忘了return,由于某些C/C++的編譯器比較敏感,會(huì)報(bào)一些警告,所以即使是void的函數(shù),我們?cè)趦?nèi)部最好也要加上return的語句,這有助于代碼的編譯。

21、goto語句的使用
---------
N年前,軟件開發(fā)的一代宗師--迪杰斯特拉(Dijkstra)說過:"goto statment is harmful !!",并建議取消goto語句。因?yàn)間oto語句不利于程序代碼的維護(hù)性。
這里我也強(qiáng)烈建議不要使用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語句會(huì)讓你的程序更易讀,更容易維護(hù)。(在用嵌C來對(duì)數(shù)據(jù)庫設(shè)置游標(biāo)操作時(shí),或是對(duì)數(shù)據(jù)庫建立鏈接時(shí),也會(huì)遇到這種結(jié)構(gòu))

22、宏的使用
------
很多程序員不知道C中的"宏"到底是什么意思?特別是當(dāng)宏有參數(shù)的時(shí)候,經(jīng)常把宏和函數(shù)混淆。我想在這里我還是先講講"宏",宏只是一種定義,他定義了一個(gè)語句塊,當(dāng)程序編譯時(shí),編譯器首先要執(zhí)行一個(gè)"替換"源程序的動(dòng)作,把宏引用的地方替換成宏定義的語句塊,就像文本文件替換一樣。這個(gè)動(dòng)作術(shù)語叫"宏的展開"
使用宏是比較"危險(xiǎn)"的,因?yàn)槟悴恢篮暾归_后會(huì)是什么一個(gè)樣子。例如下面這個(gè)宏:
  #define MAX(a, b)   a>b?a:b
當(dāng)我們這樣使用宏時(shí),沒有什么問題: MAX( num1, num2 ); 因?yàn)楹暾归_后變成 num1>num2?num1:num2;。但是,如果是這樣調(diào)用的,MAX( 17+32, 25+21 ); 呢,編譯時(shí)出現(xiàn)錯(cuò)誤,原因是,宏展開后變成:17+32>25+21?17+32:25+21,哇,這是什么。
所以,宏在使用時(shí),參數(shù)一定要加上括號(hào),上述的那個(gè)例子改成如下所示就能解決問題了。
  #define MAX( (a), (b) )   (a)>(b)?(a):(b)
  
即使是這樣,也不這個(gè)宏也還是有Bug,因?yàn)槿绻疫@樣調(diào)用 MAX(i++, j++); ,經(jīng)過這個(gè)宏以后,i和j都被累加了兩次,這絕不是我們想要的。
  所以,在宏的使用上還是要謹(jǐn)慎考慮,因?yàn)楹暾归_是的結(jié)果是很難讓人預(yù)料的。而且雖然,宏的執(zhí)行很快(因?yàn)闆]有函數(shù)調(diào)用的開銷),但宏會(huì)讓源代碼澎漲,使目標(biāo)文件尺寸變大,(如:一個(gè)50行的宏,程序中有1000個(gè)地方用到,宏展開后會(huì)很不得了),相反不能讓程序執(zhí)行得更快(因?yàn)閳?zhí)行文件變大,運(yùn)行時(shí)系統(tǒng)換頁頻繁)。
因此,在決定是用函數(shù),還是用宏時(shí)得要小心。 23、static的使用
--------
static關(guān)鍵字,表示了"靜態(tài)",一般來說,他會(huì)被經(jīng)常用于變量和函數(shù)。一個(gè)static的變量,其實(shí)就是全局變量,只不過他是有作用域的全局變量。比如一個(gè)函數(shù)中的static變量:
char*
getConsumerName()
{
  static int cnt = 0;
  
  ....
  cnt++;
  ....
}
cnt變量的值會(huì)跟隨著函數(shù)的調(diào)用次而遞增,函數(shù)退出后,cnt的值還存在,只是cnt只能在函數(shù)中才能被訪問。而cnt的內(nèi)存也只會(huì)在函數(shù)第一次被調(diào)用時(shí)才會(huì)被分配和初始化,以后每次進(jìn)入函數(shù),都不為static分配了,而直接使用上一次的值。
對(duì)于一些被經(jīng)常調(diào)用的函數(shù)內(nèi)的常量,最好也聲明成static(參見第12條)
但static的最多的用處卻不在這里,其最大的作用的控制訪問,在C中如果一個(gè)函數(shù)或是一個(gè)全局變量被聲明為static,那么,這個(gè)函數(shù)和這個(gè)全局變量,將只能在這個(gè)C文件中被訪問,如果別的C文件中調(diào)用這個(gè)C文件中的函數(shù),或是使用其中的全局(用extern關(guān)鍵字),將會(huì)發(fā)生鏈接時(shí)錯(cuò)誤。這個(gè)特性可以用于數(shù)據(jù)和程序保密。

24、函數(shù)中的代碼尺寸
----------
一個(gè)函數(shù)完成一個(gè)具體的功能,一般來說,一個(gè)函數(shù)中的代碼最好不要超過600行左右,越少越好,最好的函數(shù)一般在100行以內(nèi),300行左右的孫函數(shù)就差不多了。有證據(jù)表明,一個(gè)函數(shù)中的代碼如果超過500行,就會(huì)有和別的函數(shù)相同或是相近的代碼,也就是說,就可以再寫另一個(gè)函數(shù)。
另外,函數(shù)一般是完成一個(gè)特定的功能,千萬忌諱在一個(gè)函數(shù)中做許多件不同的事。函數(shù)的功能越單一越好,一方面有利于函數(shù)的易讀性,另一方面更有利于代碼的維護(hù)和重用,功能越單一表示這個(gè)函數(shù)就越可能給更多的程序提供服務(wù),也就是說共性就越多。
雖然函數(shù)的調(diào)用會(huì)有一定的開銷,但比起軟件后期維護(hù)來說,增加一些運(yùn)行時(shí)的開銷而換來更好的可維護(hù)性和代碼重用性,是很值得的一件事。 25、typedef的使用
---------
typedef是一個(gè)給類型起別名的關(guān)鍵字。不要小看了它,它對(duì)于你代碼的維護(hù)會(huì)有很好的作用。比如C中沒有bool,于是在一個(gè)軟件中,一些程序員使用int,一些程序員使用short,會(huì)比較混亂,最好就是用一個(gè)typedef來定義,如:
  typedef char bool;
  
一般來說,一個(gè)C的工程中一定要做一些這方面的工作,因?yàn)槟銜?huì)涉及到跨平臺(tái),不同的平臺(tái)會(huì)有不同的字長,所以利用預(yù)編譯和typedef可以讓你最有效的維護(hù)你的代碼,如下所示:
  #ifdef SOLARIS2_5
   typedef boolean_t   BOOL_T;
  #else
   typedef int      BOOL_T;
  #endif
  
  typedef short      INT16_T;
  typedef unsigned short UINT16_T;
  typedef int       INT32_T;
  typedef unsigned int  UINT32_T;
  
  #ifdef WIN32
   typedef _int64    INT64_T;
  #else
   typedef long long   INT64_T;
  #endif
  
  typedef float      FLOAT32_T;
  typedef char*      STRING_T;
  typedef unsigned char  BYTE_T;
  typedef time_t     TIME_T;
  typedef INT32_T     PID_T;
  
使用typedef的其它規(guī)范是,在結(jié)構(gòu)和函數(shù)指針時(shí),也最好用typedef,這也有利于程序的易讀和可維護(hù)性。如:
  typedef struct _hostinfo {
    HOSTID_T  host;
    INT32_T  hostId;
    STRING_T  hostType;
    STRING_T  hostModel;
    FLOAT32_T cpuFactor;
    INT32_T  numCPUs;
    INT32_T  nDisks;
    INT32_T  memory;
    INT32_T  swap;
  } HostInfo;
  typedef INT32_T (*RsrcReqHandler)(
   void *info,
   JobArray *jobs,
   AllocInfo *allocInfo,
   AllocList *allocList);
C++中這樣也是很讓人易讀的:
  typedef CArray HostInfoArray;
于是,當(dāng)我們用其定義變量時(shí),會(huì)顯得十分易讀。如:
  HostInfo* phinfo;
  RsrcReqHandler* pRsrcHand;

這種方式的易讀性,在函數(shù)的參數(shù)中十分明顯。
關(guān)鍵是在程序種使用typedef后,幾乎所有的程序中的類型聲明都顯得那么簡潔和清淅,而且易于維護(hù),這才是typedef的關(guān)鍵。


26、為常量聲明宏
--------
最好不要在程序中出現(xiàn)數(shù)字式的"硬編碼",如:
  int user[120];
  
為這個(gè)120聲明一個(gè)宏吧。為所有出現(xiàn)在程序中的這樣的常量都聲明一個(gè)宏吧。比如TimeOut的時(shí)間,最大的用戶數(shù)量,還有其它,只要是常量就應(yīng)該聲明成宏。如果,突然在程序中出現(xiàn)下面一段代碼,
  for ( i=0; i<120; i++){
    ....
  }
120是什么?為什么會(huì)是120?這種"硬編碼"不僅讓程序很讀,而且也讓程序很不好維護(hù),如果要改變這個(gè)數(shù)字,得同時(shí)對(duì)所有程序中這個(gè)120都要做修改,這對(duì)修改程序的人來說是一個(gè)很大的痛苦。所以還是把常量聲明成宏,這樣,一改百改,而且也很利于程序閱讀。
  #define MAX_USR_CNT 120
  
  for ( i=0; i
    ....
  }
這樣就很容易了解這段程序的意圖了。
有的程序員喜歡為這種變量聲明全局變量,其實(shí),全局變量應(yīng)該盡量的少用,全局變量不利于封裝,也不利于維護(hù),而且對(duì)程序執(zhí)行空間有一定的開銷,一不小心就造成系統(tǒng)換頁,造成程序執(zhí)行速度效率等問題。所以聲明成宏,即可以免去全局變量的開銷,也會(huì)有速度上的優(yōu)勢(shì)。

27、不要為宏定義加分號(hào)
-----------
有許多程序員不知道在宏定義時(shí)是否要加分號(hào),有時(shí),他們以為宏是一條語句,應(yīng)該要加分號(hào),這就錯(cuò)了。當(dāng)你知道了宏的原理,你會(huì)贊同我為會(huì)么不要為宏定義加分號(hào)的?匆粋(gè)例子:
  #define MAXNUM 1024;
這是一個(gè)有分號(hào)的宏,如果我們這樣使用:
  half = MAXNUM/2;
  
  if ( num < MAXNUM )
等等,都會(huì)造成程序的編譯錯(cuò)誤,因?yàn)?當(dāng)宏展開后,他會(huì)是這個(gè)樣子的:
  half = 1024;/2;
  
  if ( num < 1024; )
  
是的,分號(hào)也被展進(jìn)去了,所以造成了程序的錯(cuò)誤。請(qǐng)相信我,有時(shí)候,一個(gè)分號(hào)會(huì)讓你的程序出現(xiàn)成百個(gè)錯(cuò)誤。所以還是不要為宏加最后一個(gè)分號(hào),哪怕是這樣:
  #define LINE  "================================="
  
  #define PRINT_LINE printf(LINE)
  #define PRINT_NLINE(n) while ( n-- >0 ) { PRINT_LINE; }
  
都不要在最后加上分號(hào),當(dāng)我們?cè)诔绦蛑惺褂脮r(shí),為之加上分號(hào),
  main()
  {
    char *p = LINE;
    PRINT_LINE;
  }
這一點(diǎn)非常符合習(xí)慣,而且,如果忘加了分號(hào),編譯器給出的錯(cuò)誤提示,也會(huì)讓我們很容易看懂的。

28、 和&&的語句執(zhí)行順序
------------
條件語句中的這兩個(gè)"與"和"或"操作符一定要小心,它們的表現(xiàn)可能和你想像的不一樣,這里條件語句中的有些行為需要和說一下:
  express1 express2
    
  先執(zhí)行表達(dá)式express1如果為"真",express2將不被執(zhí)行,express2僅在express1為"假"時(shí)才被執(zhí)行。因?yàn)榈谝粋(gè)表達(dá)式為真了,整個(gè)表達(dá)式都為真,所以沒有必要再去執(zhí)行第二個(gè)表達(dá)式了。
  express1 && express2
  先執(zhí)行表達(dá)式express1如果為"假",express2將不被執(zhí)行,express2僅在express1為"真"時(shí)才被執(zhí)行。因?yàn)榈谝粋(gè)表達(dá)式為假了,整個(gè)表達(dá)式都為假了,所以沒有必要再去執(zhí)行第二個(gè)表達(dá)式了。
于是,他并不是你所想像的所有的表達(dá)式都會(huì)去執(zhí)行,這點(diǎn)一定要明白,不然你的程序會(huì)出現(xiàn)一些莫明的運(yùn)行時(shí)錯(cuò)誤。
例如,下面的程序:
  if ( sum > 100 &&
     ( ( fp=fopen( filename,"a" ) ) != NULL )  {
    
     fprintf(fp, "Warring: it beyond one hundred\n");
     ......
  }
  
  fprintf( fp, " sum is %id \n", sum );
  fclose( fp );
本來的意圖是,如果sum > 100 ,向文件中寫一條出錯(cuò)信息,為了方便,把兩個(gè)條件判斷寫在一起,于是,如果sum<=100時(shí),打開文件的操作將不會(huì)做,最后,fprintf和fclose就會(huì)發(fā)現(xiàn)未知的結(jié)果。
再比如,如果我想判斷一個(gè)字符是不是有內(nèi)容,我得判斷這個(gè)字符串指針是不為空(NULL)并且其內(nèi)容不能為空(Empty),一個(gè)是空指針,一個(gè)是空內(nèi)容。我也許會(huì)這樣寫:
  if ( ( p != NULL ) && ( strlen(p) != 0 ))
于是,如果p為NULL,那么strlen(p)就不會(huì)被執(zhí)行,于是,strlen也就不會(huì)因?yàn)橐粋(gè)空指針而"非法操作"或是一個(gè)"Core Dump"了。
記住一點(diǎn),條件語句中,并非所有的語句都會(huì)執(zhí)行,當(dāng)你的條件語句非常多時(shí),這點(diǎn)要尤其注意。

29、盡量用for而不是while做循環(huán)
---------------
基本上來說,for可以完成while的功能,我是建議盡量使用for語句,而不要使用while語句,特別是當(dāng)循環(huán)體很大時(shí),for的優(yōu)點(diǎn)一下就體現(xiàn)出來了。
因?yàn)樵趂or中,循環(huán)的初始、結(jié)束條件、循環(huán)的推進(jìn),都在一起,一眼看上去就知道這是一個(gè)什么樣的循環(huán)。剛出學(xué)校的程序一般對(duì)于鏈接喜歡這樣來:
  p = pHead;
  
  while ( p ){
    ...
    ...
    p = p->next;
  }
當(dāng)while的語句塊變大后,你的程序?qū)⒑茈y讀,用for就好得多:
  for ( p=pHead; p; p=p->next ){
  ..
  }
一眼就知道這個(gè)循環(huán)的開始條件,結(jié)束條件,和循環(huán)的推進(jìn)。大約就能明白這個(gè)循環(huán)要做個(gè)什么事?而且,程序維護(hù)進(jìn)來很容易,不必像while一樣,在一個(gè)編輯器中上上下下的搗騰。

30、請(qǐng)sizeof類型而不是變量
-------------
許多程序員在使用sizeof中,喜歡sizeof變量名,例如:
int score[100];
char filename[20];
struct UserInfo usr[100];
在sizeof這三個(gè)的變量名時(shí),都會(huì)返回正確的結(jié)果,于是許多程序員就開始sizeof變量名。這個(gè)習(xí)慣很雖然沒有什么不好,但我還是建議sizeof類型。
我看到過這個(gè)的程序:
  pScore = (int*) malloc( SUBJECT_CNT );
  memset( pScore, 0, sizeof(pScore) );
  ...
  
此時(shí),sizeof(pScore)返回的就是4(指針的長度),不會(huì)是整個(gè)數(shù)組,于是,memset就不能對(duì)這塊內(nèi)存進(jìn)行初始化。為了程序的易讀和易維護(hù),我強(qiáng)烈建議使用類型而不是變量,如:
對(duì)于score:   sizeof(int) * 100  /* 100個(gè)int */
對(duì)于filename: sizeof(char) * 20  /* 20個(gè)char */
對(duì)于usr:    sizeof(struct UserInfo) * 100  /* 100個(gè)UserInfo */
這樣的代碼是不是很易讀?一眼看上去就知道什么意思了。
另外一點(diǎn),sizeof一般用于分配內(nèi)存,這個(gè)特性特別在多維數(shù)組時(shí),就能體現(xiàn)出其優(yōu)點(diǎn)了。如,給一個(gè)字符串?dāng)?shù)組分配內(nèi)存,
/*
* 分配一個(gè)有20個(gè)字符串,
* 每個(gè)字符串長100的內(nèi)存
*/
char* *p;
/*
* 錯(cuò)誤的分配方法
*/
p = (char**)calloc( 20*100, sizeof(char) );
/*
* 正確的分配方法
*/
p = (char**) calloc ( 20, sizeof(char*) );
for ( i=0; i<20; i++){
  /*p = (char*) calloc ( 100, sizeof(char) );*/
  p[i] = (char*) calloc ( 100, sizeof(char) );
}
(注:上述語句被注釋掉的是原來的,是錯(cuò)誤的,由dasherest朋友指正,謝謝)
為了代碼的易讀,省去了一些判斷,請(qǐng)注意這兩種分配的方法,有本質(zhì)上的差別。

31、不要忽略Warning
----------
對(duì)于一些編譯時(shí)的警告信息,請(qǐng)不要忽視它們。雖然,這些Warning不會(huì)妨礙目標(biāo)代碼的生成,但這并不意味著你的程序就是好的。必竟,并不是編譯成功的程序才是正確的,編譯成功只是萬里長征的第一步,后面還有大風(fēng)大浪在等著你。從編譯程序開始,不但要改正每個(gè)error,還要修正每個(gè)warning。這是一個(gè)有修養(yǎng)的程序員該做的事。

一般來說,一面的一些警告信息是常見的:
  1)聲明了未使用的變量。(雖然編譯器不會(huì)編譯這種變量,但還是把它從源程序中注釋或是刪除吧)
  2)使用了隱晦聲明的函數(shù)。(也許這個(gè)函數(shù)在別的C文件中,編譯時(shí)會(huì)出現(xiàn)這種警告,你應(yīng)該這使用之前使用extern關(guān)鍵字聲明這個(gè)函數(shù))
  3)沒有轉(zhuǎn)換一個(gè)指針。(例如malloc返回的指針是void的,你沒有把之轉(zhuǎn)成你實(shí)際類型而報(bào)警,還是手動(dòng)的在之前明顯的轉(zhuǎn)換一下吧)
  4)類型向下轉(zhuǎn)換。(例如:float f = 2.0; 這種語句是會(huì)報(bào)警告的,編譯會(huì)告訴你正試圖把一個(gè)double轉(zhuǎn)成float,你正在閹割一個(gè)變量,你真的要這樣做嗎?還是在2.0后面加個(gè)f吧,不然,2.0就是一個(gè)double,而不是float了)
  
不管怎么說,編譯器的Warning不要小視,最好不要忽略,一個(gè)程序都做得出來,何況幾個(gè)小小的Warning呢?

32、書寫Debug版和Release版的程序
----------------
程序在開發(fā)過程中必然有許多程序員加的調(diào)試信息。我見過許多項(xiàng)目組,當(dāng)程序開發(fā)結(jié)束時(shí),發(fā)動(dòng)群眾刪除程序中的調(diào)試信息,何必呢?為什么不像VC++那樣建立兩個(gè)版本的目標(biāo)代碼?一個(gè)是debug版本的,一個(gè)是Release版的。那些調(diào)試信息是那么的寶貴,在日后的維護(hù)過程中也是很寶貴的東西,怎么能說刪除就刪除呢?
利用預(yù)編譯技術(shù)吧,如下所示聲明調(diào)試函數(shù):
  #ifdef DEBUG
    void TRACE(char* fmt, ...)
    {
      ......
    }
  #else
    #define TRACE(char* fmt, ...)
  #endif
于是,讓所有的程序都用TRACE輸出調(diào)試信息,只需要在在編譯時(shí)加上一個(gè)參數(shù)"-DDEBUG",如:
  cc -DDEBUG -o target target.c
于是,預(yù)編譯器發(fā)現(xiàn)DEBUG變量被定義了,就會(huì)使用TRACE函數(shù)。而如果要發(fā)布給用戶了,那么只需要把取消"-DDEBUG"的參數(shù),于是所有用到 TRACE宏,這個(gè)宏什么都沒有,所以源程序中的所有TRACE語言全部被替換成了空。一舉兩得,一箭雙雕,何樂而不為呢?
順便提一下,兩個(gè)很有用的系統(tǒng)宏,一個(gè)是"__FILE__",一個(gè)是"__LINE__",分別表示,所在的源文件和行號(hào),當(dāng)你調(diào)試信息或是輸出錯(cuò)誤時(shí),可以使用這兩個(gè)宏,讓你一眼就能看出你的錯(cuò)誤,出現(xiàn)在哪個(gè)文件的第幾行中。這對(duì)于用C/C++做的大工程非常的管用。
綜上所述32條,都是為了三大目的--
  1、程序代碼的易讀性。
  2、程序代碼的可維護(hù)性,
  3、程序代碼的穩(wěn)定可靠性。
  
有修養(yǎng)的程序員,就應(yīng)該要學(xué)會(huì)寫出這樣的代碼!這是任何一個(gè)想做編程高手所必需面對(duì)的細(xì)小的問題,編程高手不僅技術(shù)要強(qiáng),基礎(chǔ)要好,而且最重要的是要有"修養(yǎng)"!
好的軟件產(chǎn)品絕不僅僅是技術(shù),而更多的是整個(gè)軟件的易維護(hù)和可靠性! 
軟件的維護(hù)有大量的工作量花在代碼的維護(hù)上,軟件的Upgrade,也有大量的工作花在代碼的組織上,所以好的代碼,清淅的,易讀的代碼,將給大大減少軟件的維護(hù)和升級(jí)成本。

  • 上一篇: 實(shí)時(shí)多任務(wù)操作系統(tǒng)(RTOS)進(jìn)入單片機(jī)開發(fā)領(lǐng)域
  • 下一篇: 學(xué)習(xí)AVR(六)CPU中狀態(tài)寄存器
  • 發(fā)表評(píng)論   告訴好友   打印此文  收藏此頁  關(guān)閉窗口  返回頂部
    熱點(diǎn)文章
     
    推薦文章
     
    相關(guān)文章
    網(wǎng)友評(píng)論:(只顯示最新5條。)
    關(guān)于我們 | 聯(lián)系我們 | 廣告合作 | 付款方式 | 使用幫助 | 機(jī)電之家 | 會(huì)員助手 | 免費(fèi)鏈接

    點(diǎn)擊這里給我發(fā)消息66821730(技術(shù)支持)點(diǎn)擊這里給我發(fā)消息66821730(廣告投放) 點(diǎn)擊這里給我發(fā)消息41031197(編輯) 點(diǎn)擊這里給我發(fā)消息58733127(審核)
    本站提供的機(jī)電設(shè)備,機(jī)電供求等信息由機(jī)電企業(yè)自行提供,該企業(yè)負(fù)責(zé)信息內(nèi)容的真實(shí)性、準(zhǔn)確性和合法性。
    機(jī)電之家對(duì)此不承擔(dān)任何保證責(zé)任,有侵犯您利益的地方請(qǐng)聯(lián)系機(jī)電之家,機(jī)電之家將及時(shí)作出處理。
    Copyright 2007 機(jī)電之家 Inc All Rights Reserved.機(jī)電之家-由機(jī)電一體化網(wǎng)更名-聲明
    電話:0571-87774297 傳真:0571-87774298
    杭州濱興科技有限公司提供技術(shù)支持

    主辦:杭州市高新區(qū)(濱江)機(jī)電一體化學(xué)會(huì)
    中國行業(yè)電子商務(wù)100強(qiáng)網(wǎng)站

    網(wǎng)站經(jīng)營許可證:浙B2-20080178-1