2八、||和&&的語句執行順序
————————————
條件語句中的這兩個「與」和「或」操做符必定要當心,它們的表現可能和你想像的不同,這裏條件語句中的有些行爲須要和說一下:
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」了。
記住一點,條件語句中,並不是全部的語句都會執行,當你的條件語句很是多時,這點要尤爲注意。
2九、儘可能用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朋友指正,謝謝)
爲了代碼的易讀,省去了一些判斷,請注意這兩種分配的方法,有本質上的差異。
3一、不要忽略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呢?
3二、書寫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條,都是爲了三大目的——
一、程序代碼的易讀性。
二、程序代碼的可維護性,
三、程序代碼的穩定可靠性。
有修養的程序員,就應該要學會寫出這樣的代碼!這是任何一個想作編程高手所必需面對的細小的問題,編程高手不只技術要強,基礎要好,並且最重要的是要有「修養」!
好的軟件產品毫不僅僅是技術,而更多的是整個軟件的易維護和可靠性。
軟件的維護有大量的工做量花在代碼的維護上,軟件的Upgrade,也有大量的工做花在代碼的組織上,因此好的代碼,清淅的,易讀的代碼,將給大大減小軟件的維護和升級成本。