天天看點

程式設計修養(五)

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 ){

        mname = ( char* ) calloc ( 20, sizeof(char) );

        if ( mname == NULL ){

        ......

     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)

即使是這樣,也不這個宏也還是有Bug,因為如果我這樣調用 MAX(i++, j++); , 經過這個宏以後,i和j都被累加了兩次,這絕不是我們想要的。

是以,在宏的使用上還是要謹慎考慮,因為宏展開是的結果是很難讓人預料的。而且雖然,宏的執行很快(因為沒有函數調用的開銷),但宏會讓源代碼澎漲,使目标檔案尺寸變大,(如:一個50行的宏,程式中有1000個地方用到,宏展開後會很不得了),相反不能讓程式執行得更快(因為執行檔案變大,運作時系統換頁頻繁)。

是以,在決定是用函數,還是用宏時得要小心。

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