编程修养(七)
28、||和&&的語句執行順序
————————————
條件語句中的這兩個“與”和“或”操作符一定要小心,它們的表現可能和你想像的不一樣,這里條件語句中的有些行為需要和說一下:
? ? express1 || express2
? ? ? ??
? ? 先執行表達式express1如果為“真”,express2將不被執行,express2僅在express1為“假”時才被執行。因為第一個表達式為真了,整個表達式都為真,所以沒有必要再去執行第二個表達式了。
? ? express1 && express2
? ? 先執行表達式express1如果為“假”,express2將不被執行,express2僅在express1為“真”時才被執行。因為第一個表達式為假了,整個表達式都為假了,所以沒有必要再去執行第二個表達式了。
于是,他并不是你所想像的所有的表達式都會去執行,這點一定要明白,不然你的程序會出現一些莫明的運行時錯誤。
例如,下面的程序:
? ? 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 ,向文件中寫一條出錯信息,為了方便,把兩個條件判斷寫在一起,于是,如果sum<=100時,打開文件的操作將不會做,最后,fprintf和fclose就會發現未知的結果。
再比如,如果我想判斷一個字符是不是有內容,我得判斷這個字符串指針是不為空(NULL)并且其內容不能為空(Empty),一個是空指針,一個是空內容。我也許會這樣寫:
? ? if ( ( p != NULL ) && ( strlen(p) != 0 ))
于是,如果p為NULL,那么strlen(p)就不會被執行,于是,strlen也就不會因為一個空指針而“非法操作”或是一個“Core Dump”了。
記住一點,條件語句中,并非所有的語句都會執行,當你的條件語句非常多時,這點要尤其注意。
?
29、盡量用for而不是while做循環
———————————————
基本上來說,for可以完成while的功能,我是建議盡量使用for語句,而不要使用while語句,特別是當循環體很大時,for的優點一下就體現出來了。
因為在for中,循環的初始、結束條件、循環的推進,都在一起,一眼看上去就知道這是一個什么樣的循環。剛出學校的程序一般對于鏈接喜歡這樣來:
? ? p = pHead;
? ??
? ? while ( p ){
? ? ? ? ...
? ? ? ? ...
? ? ? ? p = p->next;
? ? }
當while的語句塊變大后,你的程序將很難讀,用for就好得多:
? ? for ( p=pHead; ?p; p=p->next ){
? ? ..
? ? }
一眼就知道這個循環的開始條件,結束條件,和循環的推進。大約就能明白這個循環要做個什么事?而且,程序維護進來很容易,不必像while一樣,在一個編輯器中上上下下的搗騰。
?
30、請sizeof類型而不是變量
—————————————
許多程序員在使用sizeof中,喜歡sizeof變量名,例如:
int score[100];
char filename[20];
struct UserInfo usr[100];
在sizeof這三個的變量名時,都會返回正確的結果,于是許多程序員就開始sizeof變量名。這個習慣很雖然沒有什么不好,但我還是建議sizeof類型。
我看到過這個的程序:
? ? pScore = (int*) malloc( SUBJECT_CNT );
? ? memset( pScore, 0, sizeof(pScore) );
? ? ...
? ??
此時,sizeof(pScore)返回的就是4(指針的長度),不會是整個數組,于是,memset就不能對這塊內存進行初始化。為了程序的易讀和易維護,我強烈建議使用類型而不是變量,如:
對于score: ? ? sizeof(int) * 100 ? /* 100個int */
對于filename: ?sizeof(char) * 20 ? /* 20個char */
對于usr: ? ? ? sizeof(struct UserInfo) * 100 ? /* 100個UserInfo */
這樣的代碼是不是很易讀?一眼看上去就知道什么意思了。
另外一點,sizeof一般用于分配內存,這個特性特別在多維數組時,就能體現出其優點了。如,給一個字符串數組分配內存,
/*?
?* 分配一個有20個字符串,
?* 每個字符串長100的內存?
?*/
char* *p;
/*
?* 錯誤的分配方法
?*/
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) );
}
(注:上述語句被注釋掉的是原來的,是錯誤的,由dasherest朋友指正,謝謝)
為了代碼的易讀,省去了一些判斷,請注意這兩種分配的方法,有本質上的差別。
?
31、不要忽略Warning
——————————
對于一些編譯時的警告信息,請不要忽視它們。雖然,這些Warning不會妨礙目標代碼的生成,但這并不意味著你的程序就是好的。必竟,并不是編譯成功的程序才是正確的,編譯成功只是萬里長征的第一步,后面還有大風大浪在等著你。從編譯程序開始,不但要改正每個error,還要修正每個warning。這是一個有修養的程序員該做的事。
一般來說,一面的一些警告信息是常見的:
? ? 1)聲明了未使用的變量。(雖然編譯器不會編譯這種變量,但還是把它從源程序中注釋或是刪除吧)
? ? 2)使用了隱晦聲明的函數。(也許這個函數在別的C文件中,編譯時會出現這種警告,你應該這使用之前使用extern關鍵字聲明這個函數)
? ? 3)沒有轉換一個指針。(例如malloc返回的指針是void的,你沒有把之轉成你實際類型而報警,還是手動的在之前明顯的轉換一下吧)
? ? 4)類型向下轉換。(例如:float f = 2.0; 這種語句是會報警告的,編譯會告訴你正試圖把一個double轉成float,你正在閹割一個變量,你真的要這樣做嗎?還是在2.0后面加個f吧,不然,2.0就是一個double,而不是float了)
? ??
不管怎么說,編譯器的Warning不要小視,最好不要忽略,一個程序都做得出來,何況幾個小小的Warning呢?
?
32、書寫Debug版和Release版的程序
————————————————
程序在開發過程中必然有許多程序員加的調試信息。我見過許多項目組,當程序開發結束時,發動群眾刪除程序中的調試信息,何必呢?為什么不像VC++那樣建立兩個版本的目標代碼?一個是debug版本的,一個是Release版的。那些調試信息是那么的寶貴,在日后的維護過程中也是很寶貴的東西,怎么能說刪除就刪除呢?
利用預編譯技術吧,如下所示聲明調試函數:
? ? #ifdef DEBUG
? ? ? ? void TRACE(char* fmt, ...)
? ? ? ? {
? ? ? ? ? ? ......
? ? ? ? }
? ? #else
? ? ? ? #define TRACE(char* fmt, ...)
? ? #endif
于是,讓所有的程序都用TRACE輸出調試信息,只需要在在編譯時加上一個參數“-DDEBUG”,如:
? ? cc -DDEBUG -o target target.c
于是,預編譯器發現DEBUG變量被定義了,就會使用TRACE函數。而如果要發布給用戶了,那么只需要把取消“-DDEBUG”的參數,于是所有用到TRACE宏,這個宏什么都沒有,所以源程序中的所有TRACE語言全部被替換成了空。一舉兩得,一箭雙雕,何樂而不為呢?
順便提一下,兩個很有用的系統宏,一個是“__FILE__”,一個是“__LINE__”,分別表示,所在的源文件和行號,當你調試信息或是輸出錯誤時,可以使用這兩個宏,讓你一眼就能看出你的錯誤,出現在哪個文件的第幾行中。這對于用C/C++做的大工程非常的管用。
綜上所述32條,都是為了三大目的——
? ? 1、程序代碼的易讀性。
? ? 2、程序代碼的可維護性,
? ? 3、程序代碼的穩定可靠性。
? ??
有修養的程序員,就應該要學會寫出這樣的代碼!這是任何一個想做編程高手所必需面對的細小的問題,編程高手不僅技術要強,基礎要好,而且最重要的是要有“修養”!
好的軟件產品絕不僅僅是技術,而更多的是整個軟件的易維護和可靠性。 ??
軟件的維護有大量的工作量花在代碼的維護上,軟件的Upgrade,也有大量的工作花在代碼的組織上,所以好的代碼,清淅的,易讀的代碼,將給大大減少軟件的維護和升級成本。
本文摘自:http://blog.csdn.net/haoel/article/details/2878
總結
- 上一篇: 编程修养(六)
- 下一篇: 雅马哈天戟耐用吗(YAMAHA雅马哈官方