天天看點

程式設計修養(四)

11、出錯資訊的處理

—————————

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

    if ( p == NULL ){

        printf ( "ERR: The pointer is NULL\n" );

    }

告别學生時代的程式設計吧。這種程式設計很不利于維護和管理,出錯資訊或是提示資訊,應該統一處理,而不是像上面這樣,寫成一個“寫死”。第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_PERMISSION  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\n", info, errmsg[errno] );

            return;

        }

        printf("Error: %s\n", errmsg[errno] );

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

    bool CheckPermission( char* userName )

        if ( strcpy(userName, "root") != 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%的知道别人的意圖,是以為了可以恢複,請不依賴于CVS或是SourceSafe這種版本控制軟體,還是要在源碼上給别人看到你修改程式的意圖和步驟。這是程式維護時,一個有修養的程式員所應該做的。

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

    /*

     * ----- 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 );

    /* ---------------------------------------- */

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

以“注釋 — 添加”方式修改别人的程式,要好于直接删除别人的程式。

本文轉自 haoel 51CTO部落格,原文連結:http://blog.51cto.com/haoel/124710,如需轉載請自行聯系原作者

繼續閱讀