编程修养(五)
16、把相同或近乎相同的代碼形成函數(shù)和宏
—————————————————————
有人說,最好的程序員,就是最喜歡“偷懶”的程序,其中不無道理。
如果你有一些程序的代碼片段很相似,或直接就是一樣的,請把他們放在一個函數(shù)中。而如果這段代碼不多,而且會被經(jīng)常使用,你還想避免函數(shù)調(diào)用的開銷,那么就把他寫成宏吧。
千萬不要讓同一份代碼或是功能相似的代碼在多個地方存在,不然如果功能一變,你就要修改好幾處地方,這種會給維護帶來巨大的麻煩,所以,做到“一改百改”,還是要形成函數(shù)或是宏。
?
17、表達式中的括號
—————————
如果一個比較復(fù)雜的表達式中,你并不是很清楚各個操作符的憂先級,即使是你很清楚優(yōu)先級,也請加上括號,不然,別人或是自己下一次讀程序時,一不小心就看走眼理解錯了,為了避免這種“誤解”,還有讓自己的程序更為清淅,還是加上括號吧。
比如,對一個結(jié)構(gòu)的成員取地址:
? ? GetUserAge( &( UserInfo->age ) );
雖然,&UserInfo->age中,->操作符的優(yōu)先級最高,但加上一個括號,會讓人一眼就看明白你的代碼是什么意思。
再比如,一個很長的條件判斷:
if ( ( ch[0] >= '0' || ch[0] <= '9' ) &&
? ? ?( ch[1] >= 'a' || ch[1] <= 'z' ) &&
? ? ?( ch[2] >= 'A' || ch[2] <= 'Z' ) ? ?)
? ? ?
括號,再加上空格和換行,你的代碼是不是很容易讀懂了? ? ?
18、函數(shù)參數(shù)中的const
———————————
對于一些函數(shù)中的指針參數(shù),如果在函數(shù)中只讀,請將其用const修飾,這樣,別人一讀到你的函數(shù)接口時,就會知道你的意圖是這個參數(shù)是[in],如果沒有const時,參數(shù)表示[in/out],注意函數(shù)接口中的const使用,利于程序的維護和避免犯一些錯誤。
雖然,const修飾的指針,如:const char* p,在C中一點用也沒有,因為不管你的聲明是不是const,指針的內(nèi)容照樣能改,因為編譯器會強制轉(zhuǎn)換,但是加上這樣一個說明,有利于程序的閱讀和編譯。因為在C中,修改一個const指針所指向的內(nèi)存時,會報一個Warning。這會引起程序員的注意。
C++中對const定義的就很嚴格了,所以C++中要多多的使用const,const的成員函數(shù),const的變量,這樣會對讓你的代碼和你的程序更加完整和易讀。(關(guān)于C++的const我就不多說了)
?
19、函數(shù)的參數(shù)個數(shù)(多了請用結(jié)構(gòu))
—————————————————
函數(shù)的參數(shù)個數(shù)最好不要太多,一般來說6個左右就可以了,眾多的函數(shù)參數(shù)會讓讀代碼的人一眼看上去就很頭昏,而且也不利于維護。如果參數(shù)眾多,還請使用結(jié)構(gòu)來傳遞參數(shù)。這樣做有利于數(shù)據(jù)的封裝和程序的簡潔性。
也利于使用函數(shù)的人,因為如果你的函數(shù)個數(shù)很多,比如12個,調(diào)用者很容易搞錯參數(shù)的順序和個數(shù),而使用結(jié)構(gòu)struct來傳遞參數(shù),就可以不管參數(shù)的順序。
而且,函數(shù)很容易被修改,如果需要給函數(shù)增加參數(shù),不需要更改函數(shù)接口,只需更改結(jié)構(gòu)體和函數(shù)內(nèi)部處理,而對于調(diào)用函數(shù)的程序來說,這個動作是透明的。
?
20、函數(shù)的返回類型,不要省略
——————————————
我看到很多程序?qū)懞瘮?shù)時,在函數(shù)的返回類型方面不太注意。如果一個函數(shù)沒有返回值,也請在函數(shù)前面加上void的修飾。而有的程序員偷懶,在返回int的函數(shù)則什么不修飾(因為如果不修飾,則默認返回int),這種習(xí)慣很不好,還是為了原代碼的易讀性,加上int吧。
所以函數(shù)的返回值類型,請不要省略。
另外,對于void的函數(shù),我們往往會忘了return,由于某些C/C++的編譯器比較敏感,會報一些警告,所以即使是void的函數(shù),我們在內(nèi)部最好也要加上return的語句,這有助于代碼的編譯。
?
21、goto語句的使用
—————————
N年前,軟件開發(fā)的一代宗師——迪杰斯特拉(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來對數(shù)據(jù)庫設(shè)置游標操作時,或是對數(shù)據(jù)庫建立鏈接時,也會遇到這種結(jié)構(gòu))
?
22、宏的使用
——————
很多程序員不知道C中的“宏”到底是什么意思?特別是當(dāng)宏有參數(shù)的時候,經(jīng)常把宏和函數(shù)混淆。我想在這里我還是先講講“宏”,宏只是一種定義,他定義了一個語句塊,當(dāng)程序編譯時,編譯器首先要執(zhí)行一個“替換”源程序的動作,把宏引用的地方替換成宏定義的語句塊,就像文本文件替換一樣。這個動作術(shù)語叫“宏的展開”
使用宏是比較“危險”的,因為你不知道宏展開后會是什么一個樣子。例如下面這個宏:
? ? #define ?MAX(a, b) ? ? a>b?a:b
當(dāng)我們這樣使用宏時,沒有什么問題: MAX( num1, num2 ); 因為宏展開后變成 num1>num2?num1:num2;。 但是,如果是這樣調(diào)用的,MAX( 17+32, 25+21 ); 呢,編譯時出現(xiàn)錯誤,原因是,宏展開后變成:17+32>25+21?17+32:25+21,哇,這是什么啊?
所以,宏在使用時,參數(shù)一定要加上括號,上述的那個例子改成如下所示就能解決問題了。
? ? #define ?MAX( (a), (b) ) ? ? (a)>(b)?(a):(b)
? ??
即使是這樣,也不這個宏也還是有Bug,因為如果我這樣調(diào)用 MAX(i++, j++); , 經(jīng)過這個宏以后,i和j都被累加了兩次,這絕不是我們想要的。
? ??
所以,在宏的使用上還是要謹慎考慮,因為宏展開是的結(jié)果是很難讓人預(yù)料的。而且雖然,宏的執(zhí)行很快(因為沒有函數(shù)調(diào)用的開銷),但宏會讓源代碼澎漲,使目標文件尺寸變大,(如:一個50行的宏,程序中有1000個地方用到,宏展開后會很不得了),相反不能讓程序執(zhí)行得更快(因為執(zhí)行文件變大,運行時系統(tǒng)換頁頻繁)。
—————————————————————
有人說,最好的程序員,就是最喜歡“偷懶”的程序,其中不無道理。
如果你有一些程序的代碼片段很相似,或直接就是一樣的,請把他們放在一個函數(shù)中。而如果這段代碼不多,而且會被經(jīng)常使用,你還想避免函數(shù)調(diào)用的開銷,那么就把他寫成宏吧。
千萬不要讓同一份代碼或是功能相似的代碼在多個地方存在,不然如果功能一變,你就要修改好幾處地方,這種會給維護帶來巨大的麻煩,所以,做到“一改百改”,還是要形成函數(shù)或是宏。
?
17、表達式中的括號
—————————
如果一個比較復(fù)雜的表達式中,你并不是很清楚各個操作符的憂先級,即使是你很清楚優(yōu)先級,也請加上括號,不然,別人或是自己下一次讀程序時,一不小心就看走眼理解錯了,為了避免這種“誤解”,還有讓自己的程序更為清淅,還是加上括號吧。
比如,對一個結(jié)構(gòu)的成員取地址:
? ? GetUserAge( &( UserInfo->age ) );
雖然,&UserInfo->age中,->操作符的優(yōu)先級最高,但加上一個括號,會讓人一眼就看明白你的代碼是什么意思。
再比如,一個很長的條件判斷:
if ( ( ch[0] >= '0' || ch[0] <= '9' ) &&
? ? ?( ch[1] >= 'a' || ch[1] <= 'z' ) &&
? ? ?( ch[2] >= 'A' || ch[2] <= 'Z' ) ? ?)
? ? ?
括號,再加上空格和換行,你的代碼是不是很容易讀懂了? ? ?
18、函數(shù)參數(shù)中的const
———————————
對于一些函數(shù)中的指針參數(shù),如果在函數(shù)中只讀,請將其用const修飾,這樣,別人一讀到你的函數(shù)接口時,就會知道你的意圖是這個參數(shù)是[in],如果沒有const時,參數(shù)表示[in/out],注意函數(shù)接口中的const使用,利于程序的維護和避免犯一些錯誤。
雖然,const修飾的指針,如:const char* p,在C中一點用也沒有,因為不管你的聲明是不是const,指針的內(nèi)容照樣能改,因為編譯器會強制轉(zhuǎn)換,但是加上這樣一個說明,有利于程序的閱讀和編譯。因為在C中,修改一個const指針所指向的內(nèi)存時,會報一個Warning。這會引起程序員的注意。
C++中對const定義的就很嚴格了,所以C++中要多多的使用const,const的成員函數(shù),const的變量,這樣會對讓你的代碼和你的程序更加完整和易讀。(關(guān)于C++的const我就不多說了)
?
19、函數(shù)的參數(shù)個數(shù)(多了請用結(jié)構(gòu))
—————————————————
函數(shù)的參數(shù)個數(shù)最好不要太多,一般來說6個左右就可以了,眾多的函數(shù)參數(shù)會讓讀代碼的人一眼看上去就很頭昏,而且也不利于維護。如果參數(shù)眾多,還請使用結(jié)構(gòu)來傳遞參數(shù)。這樣做有利于數(shù)據(jù)的封裝和程序的簡潔性。
也利于使用函數(shù)的人,因為如果你的函數(shù)個數(shù)很多,比如12個,調(diào)用者很容易搞錯參數(shù)的順序和個數(shù),而使用結(jié)構(gòu)struct來傳遞參數(shù),就可以不管參數(shù)的順序。
而且,函數(shù)很容易被修改,如果需要給函數(shù)增加參數(shù),不需要更改函數(shù)接口,只需更改結(jié)構(gòu)體和函數(shù)內(nèi)部處理,而對于調(diào)用函數(shù)的程序來說,這個動作是透明的。
?
20、函數(shù)的返回類型,不要省略
——————————————
我看到很多程序?qū)懞瘮?shù)時,在函數(shù)的返回類型方面不太注意。如果一個函數(shù)沒有返回值,也請在函數(shù)前面加上void的修飾。而有的程序員偷懶,在返回int的函數(shù)則什么不修飾(因為如果不修飾,則默認返回int),這種習(xí)慣很不好,還是為了原代碼的易讀性,加上int吧。
所以函數(shù)的返回值類型,請不要省略。
另外,對于void的函數(shù),我們往往會忘了return,由于某些C/C++的編譯器比較敏感,會報一些警告,所以即使是void的函數(shù),我們在內(nèi)部最好也要加上return的語句,這有助于代碼的編譯。
?
21、goto語句的使用
—————————
N年前,軟件開發(fā)的一代宗師——迪杰斯特拉(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來對數(shù)據(jù)庫設(shè)置游標操作時,或是對數(shù)據(jù)庫建立鏈接時,也會遇到這種結(jié)構(gòu))
?
22、宏的使用
——————
很多程序員不知道C中的“宏”到底是什么意思?特別是當(dāng)宏有參數(shù)的時候,經(jīng)常把宏和函數(shù)混淆。我想在這里我還是先講講“宏”,宏只是一種定義,他定義了一個語句塊,當(dāng)程序編譯時,編譯器首先要執(zhí)行一個“替換”源程序的動作,把宏引用的地方替換成宏定義的語句塊,就像文本文件替換一樣。這個動作術(shù)語叫“宏的展開”
使用宏是比較“危險”的,因為你不知道宏展開后會是什么一個樣子。例如下面這個宏:
? ? #define ?MAX(a, b) ? ? a>b?a:b
當(dāng)我們這樣使用宏時,沒有什么問題: MAX( num1, num2 ); 因為宏展開后變成 num1>num2?num1:num2;。 但是,如果是這樣調(diào)用的,MAX( 17+32, 25+21 ); 呢,編譯時出現(xiàn)錯誤,原因是,宏展開后變成:17+32>25+21?17+32:25+21,哇,這是什么啊?
所以,宏在使用時,參數(shù)一定要加上括號,上述的那個例子改成如下所示就能解決問題了。
? ? #define ?MAX( (a), (b) ) ? ? (a)>(b)?(a):(b)
? ??
即使是這樣,也不這個宏也還是有Bug,因為如果我這樣調(diào)用 MAX(i++, j++); , 經(jīng)過這個宏以后,i和j都被累加了兩次,這絕不是我們想要的。
? ??
所以,在宏的使用上還是要謹慎考慮,因為宏展開是的結(jié)果是很難讓人預(yù)料的。而且雖然,宏的執(zhí)行很快(因為沒有函數(shù)調(diào)用的開銷),但宏會讓源代碼澎漲,使目標文件尺寸變大,(如:一個50行的宏,程序中有1000個地方用到,宏展開后會很不得了),相反不能讓程序執(zhí)行得更快(因為執(zhí)行文件變大,運行時系統(tǒng)換頁頻繁)。
因此,在決定是用函數(shù),還是用宏時得要小心。
本文轉(zhuǎn)自:http://blog.csdn.net/haoel/article/details/2876
總結(jié)
- 上一篇: 编程修养(三)
- 下一篇: golang微服务网关一:网络基础知识扫