C 語言入門/C 語言常見程式設計問題
C 語言中存在一些常見的程式設計陷阱,即使是經驗豐富的程式設計師也會陷入其中
1: 將“=”(賦值運算子)與“==”(等號運算子)混淆。例如
if ( x = 1 ){ /* wrong! */
}
...
if ( x == 1 ){ /* good. */
}
以及
for ( x == 1; ... /* wrong! */ ... for ( x = 1; ... /* good. */
2: 混淆表示式中運算子的優先順序。如有疑問,請使用括號來強制優先順序。使用一些額外的括號絕不會有害。
3: 混淆結構成員運算子。如果“struct_val”是一個結構體,而“struct_ptr”是指向該結構體的指標,那麼
struct_val->myname
是錯誤的,
struct_ptr.myname
也是錯誤的。
4: 對“printf()”和“scanf()”使用不正確的格式程式碼。例如,使用“%f”列印“int”會導致奇怪的輸出。
int data[20];
...
for ( x = 1; x <= 20; ++x )
{
printf( "%d\n", data[x] );
}
5: 記住陣列的實際基索引為 0,最後一個索引比宣告的大小小 1。例如
當“x”為 20 時,將給出無效的結果。由於 C 語言不執行邊界檢查,因此這可能很難發現。
6: 混淆多維陣列的語法。如果
data[10][10]
是一個二維陣列,那麼
data[2][7]
將選擇該陣列中的一個元素。但是
data[ 2, 7 ]
將給出無效的結果,但不會被 C 語言標記為錯誤。
7: 混淆字串和字元常量。以下是字串
"Y"
與字元常量
'Y'
相反。這可能會在將字串與字元常量進行比較時造成麻煩。
8: 忘記字串以空字元 ('\0') 結尾。這意味著字串將始終比它儲存的文字大一個字元。如果字串是逐字元建立的,並且程式沒有在末尾新增空字元,也會導致問題。
9: 忘記為字串分配足夠的記憶體——或者,如果聲明瞭指標,則完全沒有為其分配任何記憶體。
10: 忘記檢查庫函式的返回值。大多數庫函式都返回一個錯誤程式碼;雖然可能不希望檢查每次“printf()”呼叫,但在關鍵操作中要小心不要忽略錯誤程式碼。
當然,忘記儲存函式返回的值(當這是獲取該值的唯一方法時)是一個愚蠢的行為,但人們偶爾會做這樣的事。
11: 擁有重複的庫函式名稱。編譯器不會始終捕獲此類錯誤。
12: 忘記為庫函式指定標頭檔案。
13: 將變數作為引數傳遞給函式,而應該傳遞指標,反之亦然。如果函式透過引數返回值,則意味著它必須指定為指標
myfunc( &myvar );
以下將不起作用
myfunc( myvar );
記住,即使函式不返回值,也可能需要指標作為引數,不過作為規則,這不是一個好的程式設計實踐。
14: 在使用巢狀的“if”和“else”語句時感到困惑。避免此問題的最佳方法是始終使用括號。避免複雜的“if”結構也是一個好主意;如果有選擇,請使用“switch”。即使對於簡單的“if”語句,使用“switch”也很有用,因為它更容易擴充套件結構,如果需要的話。
15: 忘記分號——儘管編譯器通常會捕獲這一點——或者在不應該出現的地方新增分號——它通常不會這樣做。例如
for( x = 1; x < 10; ++x );
{
printf( "%d\n", x )
}
永遠不會列印任何內容。
16: 忘記“switch”結構中的“break”語句。如前所述,這樣做只會導致執行從“switch”的一個子句流向另一個子句。
17: 粗心地混合和濫用有符號和無符號值,或不同資料型別。這可能會導致一些非常微妙的錯誤。要注意的一個特殊問題是將單個字元變數宣告為“unsigned char”。許多 I/O 函式將期望“unsigned int”型別的返回值,並且無法正確地標記 EOF。建議將函式引數強制轉換為適當的型別,即使看起來型別轉換會自行處理也是如此。
18: 變數名稱混淆。為了確保程式碼的可移植性,建議此類識別符號的前六個字元是唯一的。
19: 通常,過於複雜和巧妙的程式碼。程式是令人討厭的東西,即使它有效,也必須修改甚至移植到不同的語言。保持乾淨的結構,做簡單直接的事情,除非它帶來了不可接受的代價。