C程式設計最佳實踐

2021-04-16 09:06:28 字數 3308 閱讀 9276

意想不到的麻煩。

各種 c 實現通常在某些方面各有不同。堅持使用語言中可能對所有實現都是公共的部分會有幫助。通過這樣做,您更容易將程式移植到新的機器或編譯器,並且不大會遇到編譯器特殊性所帶來的問題。例如,考慮字串:

/*/*/2*/**/1

這裡利用了「最大適合(maximal munch)」規則。如果可以巢狀注釋,則可將該字串解釋為:

/*  /*  /2  */  *  */  1

兩個 /* 符號與兩個 */ 符號匹配,因此該字串的值為 1。如果注釋不巢狀,那麼在有些系統上,注釋中的 /* 就被忽略。在另一些系統上會針對 /* 發出警告。無論哪種情況,該表示式可解釋為:

/*  /  */  2  *  /*  */  1

2 * 1 求值得 2。

清空輸出緩衝區

當應用程式異常終止時,其輸出的尾部常常會丟失。應用程式可能沒有機會完全清空它的輸出緩衝區。輸出的某一部分可能仍在記憶體中,並且永遠不會被寫出。在有些系統上,這一輸出可能有幾頁長。

以這種方式丟失輸出會使人誤解,因為它給人的印象是程式在它實際失敗很久之前就失敗了。解決這一問題的方法是強制將輸出從緩衝區清除,特別是在除錯期間。確切的方法隨系統的不同而有所不同,不過也有常用的方法,如下所示:

setbuf(stdout, (char *) 0);

必須在將任何內容寫到標準輸出之前執行該語句。理想情況下,這將是主程式中的第一條語句。

getchar() — 巨集還是函式

以下程式將其輸入複製到其輸出:

#include  

int main(void)

從該程式除去 #include 語句將使該程式無法編譯,因為 eof 將是未定義的。

我們可以用以下方法重新編寫該程式:

#define eof -1

int main(void)

這在許多系統上都可行,但在有些系統上執行要慢很多。

因為函式呼叫通常要花較長時間,所以常常把 getchar實現為巨集。這個巨集定義在 stdio.h中,所以當除去 #include 時,編譯器就不知道 getchar 是什麼。在有些系統上,假設 getchar 是返回乙個 int的函式。

實際上,許多 c 實現在其庫中都有 getchar函式,部分原因是為了防止這樣的失誤。於是,在 #include 遺漏的情況下,編譯器使用 getchar的函式版本。函式呼叫的開銷使程式變慢。 putchar有同樣的問題。

空指標

空指標不指向任何物件。因此,為了賦值和比較以外的目的而使用空指標都是非法的。

不要重新定義 null 符號。null 符號應始終是常量值零。任何給定型別的空指標總是等於常量零,而與值為零的變數或與某一非零常量的比較,其行為由實現定義。

反引用 null 指標可能會導致奇怪的事情發生。

a+++++b 表示什麼?

解析它的唯一有意義的方法是:

a ++  +  ++  b

然而,「最大適合」規則要求將它分解為:

a ++  ++  +  b

這在語法上是無效的:它等於:

((a++)++) +  b

但 a++ 的結果不是 左值, 因此作為 ++ 的運算元是不可接受的。於是,解析詞法不明確性的規則使得以語法上有意義的方式解析該示例變得不可能。當然,謹慎的辦法實際上是在不能完全確定它們的意義 的情況下,避免這樣的構造。當然,新增空格有助於編譯器理解語句的意圖,但(從**維護的角度看)將這一構造分割成多行更可取:

++b;

(a++) + b;

小心處理函式

函式是 c 中最常用的結構概念。它們應用於實現「自頂向下的」問題解決方法 — 即,將問題分解成越來越小的子問題,直到每個子問題都能夠用**表示。這對程式的模組化和文件記錄有幫助。此外,由許多小函式組成的程式更易於除錯。

如果有一些函式引數還不是期望的型別,則將它們強制轉換為期望的型別,即使您確信沒有必要也應該這樣做,因為(如果不轉換的話)它們可能在您最意料不到的 時候給您帶來麻煩。換句話說,編譯器通常將函式引數的型別提公升和轉換成期望的資料型別以符合函式引數的宣告。但是,在**中以手工方式這樣做可以清楚地說 明程式設計師的意圖,並且在將**移植到其它平台時能確保有正確的結果。

如果標頭檔案未能宣告庫函式的返回型別,那就自己宣告它們。用 #ifdef/#endif 語句將您的宣告括起來,以備**被移植到另乙個平台。

函式原型應當用來使**更健壯,使它執行得更快。

懸空 else

除非知道自己在做什麼,否則應避免「懸空 else」問題:

if (a == 1)

if (b == 2)

printf("***/n");

else

printf("###/n");

規則是 else 附加至最近的 if。當有疑慮時,或有不明確的可能時,新增花括號以說明**的塊結構。

陣列界限

檢查所有陣列的陣列界限,包括字串,因為在您現在輸入「fubar」的地方,有人可能會輸入「floccinaucinihilipilification」。健壯的軟體產品不應使用 gets()。

c 下標以零作為開始的這一事實使所有的計數問題變得更簡單。然而,掌握如何處理它們需要花些努力。

空語句

for 或 while 迴圈的空語句體應當單獨位於一行並加上注釋,這樣就表明這個空語句體是有意放置的,而不是遺漏了**。

while (*dest++ = *src++)

;   /* void */

測試真(true)還是假(false)

不要以預設方式測試非零值,即:

if (f() != fail)

優於if (f())

儘管 fail 的值可能是 0(在 c 中視為假(false))。(當然,應當在這一風格與「函式名」一節中演示的構造之間作出權衡。)當以後有人認為失敗的返回值應該是 -1 而不是 0 時,顯式的測試對您會有幫助。

常見的問題是使用 strcmp 函式測試字串是否相等,決不應該以預設方式處理它的結果。更可取的方法是定義巨集 streq:

#define streq(str1, str2) (strcmp((str1), (str2)) == 0)

用這種方法,語句

if ( streq( inputstring, somestring ) ) ...

就具有隱含的行為,該行為不大會在您不知情的情況下改變(人們往往不會重新編寫或重新定義象 strcmp()這樣的標準庫函式)。

不要用 1 檢查相等性的布林值(true 和 yes 等);而要用 0 測試不等性(false 和 no 等)。絕大多數函式被確保在條件為假(false)時返回 0,但僅在條件為真(true)時才返回非零。因此,最好將

if (func() == true) {...

寫成if (func() != false)

C 程式設計最佳實踐 二 git操作實踐

本篇用於總結工作中常用的git操作,持續更新 1,將自己的git add 到快取,然後git commit m 單元測試 到本地倉庫 2,然後用自己的分支合併git merge dev wuwei,然後再看一下 3,解決衝突,vim或者notpad 4,再次git add 到快取,然後git com...

java程式設計最佳實踐

不應該像下面這樣 string ordername urlencoder.encode 這裡是中文 constant.charset utf finally語句只能做如下事件 1 關閉io資源,比如關閉inputstream或socket 2 只做列印錯誤資訊,捕獲異常,不要丟擲異常 儲存在資料庫中...

Bshell程式設計最佳實踐

set e set o errexit 令你的指令碼有命令失敗時,退出指令碼 command true 如果你允許命令失敗 set u set o nounset 當指令碼中存在未定義的變數時,退出指令碼 o pipefail 如果指令碼中存在管道,則捕獲管道失敗,如果失敗則退出並返回非0值。usr...