程式設計修養(七)

2021-07-25 22:52:56 字數 3998 閱讀 1339

28、||和&&的語句執行順序

————————————

條件語句中的這兩個「與」和「或」操作符一定要小心,它們的表現可能和你想像的不一樣,這裡條件語句中的有些行為需要和說一下:

express1 || express2

先執行表示式express1如果為「真」,express2將不被執行,express2僅在express1為「假」時才被執行。因為第乙個表示式為真了,整個表示式都為真,所以沒有必要再去執行第二個表示式了。

express1 && express2

先執行表示式express1如果為「假」,express2將不被執行,express2僅在express1為「真」時才被執行。因為第乙個表示式為假了,整個表示式都為假了,所以沒有必要再去執行第二個表示式了。

於是,他並不是你所想像的所有的表示式都會去執行,這點一定要明白,不然你的程式會出現一些莫明的執行時錯誤。

例如,下面的程式:

if ( sum > 100 &&

( ( fp=fopen( filename,"a" ) ) != null )  

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的優點一下就體現出來了。

p = phead;

while ( p )

當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++)

(注:上述語句被注釋掉的是原來的,是錯誤的,由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

cc -ddebug -o target target.c

於是,預編譯器發現debug變數被定義了,就會使用trace函式。而如果要發布給使用者了,那麼只需要把取消「-ddebug」的引數,於是所有用到trace巨集,這個巨集什麼都沒有,所以源程式中的所有trace語言全部被替換成了空。一舉兩得,一箭雙鵰,何樂而不為呢?

順便提一下,兩個很有用的系統巨集,乙個是「__file__」,乙個是「__line__」,分別表示,所在的原始檔和行號,當你除錯資訊或是輸出錯誤時,可以使用這兩個巨集,讓你一眼就能看出你的錯誤,出現在哪個檔案的第幾行中。這對於用c/c++做的大工程非常的管用。

綜上所述32條,都是為了三大目的——

1、程式**的易讀性。

2、程式**的可維護性,

3、程式**的穩定可靠性。

有修養的程式設計師,就應該要學會寫出這樣的**!這是任何乙個想做程式設計高手所必需面對的細小的問題,程式設計高手不僅技術要強,基礎要好,而且最重要的是要有「修養」!

好的軟體產品絕不僅僅是技術,而更多的是整個軟體的易維護和可靠性。   

軟體的維護有大量的工作量花在**的維護上,軟體的upgrade,也有大量的工作花在**的組織上,所以好的**,清淅的,易讀的**,將給大大減少軟體的維護和公升級成本。

程式設計修養 七

28 和 的語句執行順序 條件語句中的這兩個 與 和 或 操作符一定要小心,它們的表現可能和你想像的不一樣,這裡條件語句中的有些行為需要和說一下 express1 express2 先執行表示式express1如果為 真 express2將不被執行,express2僅在express1為 假 時才被...

程式設計修養(七)

28 和 的語句執行順序 條件語句中的這兩個 與 和 或 操作符一定要小心,它們的表現可能和你想像的不一樣,這裡條件語句中的有些行為需要和說一下 express1 express2 先執行表示式express1如果為 真 express2將不被執行,express2僅在express1為 假 時才被...

程式設計修養(七)

28 和 的語句執行順序 條件語句中的這兩個 與 和 或 操作符一定要小心,它們的表現可能和你想像的不一樣,這裡條件語句中的有些行為需要和說一下 express1 express2 先執行表示式express1如果為 真 express2將不被執行,express2僅在express1為 假 時才被...