正確編寫C++程式碼的10個要點(2-1)
《深入淺出C++(第2版)》作者的原文:Top Ten Tips for Correct C++ Coding
1:不要搞混賦值(=)與相等測試(==)
儘管可能會難住福爾摩斯,但這一條確實太初級了。假如C++再像一點BASIC,那麼下面的語句應該是無害的,而且可以正常編譯執行:
if (a = b)
cout << "a is equal b.";
由於表面上看不出有什麼問題,因此這種寫法在大程式中就會造成難以追蹤的邏輯錯誤——除非你有意尋找。(我在除錯程式時,首先會查這種問題。)在C和C++中,下面的表示式並非在測試兩個變數是否相等:
a = b
當然,這個表示式的意思是把b的值賦給a,然後再求出所賦的值。
問題在於,對a = b求值的結果通常得不到合理的true/false條件——稍後我會提到一個明顯的例外。但在C和C++中,任何數值都可以用作if或while語句的條件。
假設a和b的值都是0。前面那條if語句的作用就是把b的值放到a中,然後對錶達式a = b求值得到0,而0等同於false。結果,a和b本來相等,但列印出來的訊息卻錯誤的:
if (a = b) //這個條件本來是用於確保a和b相等的
cout << "a and b are equal.";
else
coun << "a and b are not equal."; //但列印出來的是這個訊息
解決方案當然是有意識地使用相等測試。注意相等測試使用兩個等號,這樣的條件才是正確的:
//正確的寫法
if (a == b)
cout << "a and b are equal.";
2:別用“魔法數字”
所謂魔法數字,指的是在程式裡突然出現一個數字,但沒有任何解釋。大多數老到的程式設計師都願意在程式裡看到像MAX_ROWS、SCREEN_WIDTH這樣有意義的符號名。
簡言之,專業的計算機程式設計師——包括一些對數學情有獨鍾的人,都很討厭數字。
瞭解一點歷史有助理解為什麼。那是在1940年代,所有人程式設計使用的都是機器碼,程式全都是位模式。生活在十八層地獄第十八層的程式設計師必須不斷地翻譯這些模式。而當組合語言中引入了符號名之後,程式設計一下子簡單了一千倍。
即使是到了今天,程式設計師們也不喜歡類似這樣的宣告:
char input_string[81];
這裡的81就是一個“魔法數字”。這個數是從哪兒來的?更好的做法是使用#define或enum語句來控制數字的使用。
#define SCREEN_WIDTH 80
SCREEN_WIDTH比81更好理解,如果你想將來重置這個寬度,只要修改一行程式碼即可。而且修改之後就能夠自動地反映到類似下面這樣的語句當中:
int input_string[SCREEN_WIDTH + 1];
3:別太依賴整數除法(除非你就想那麼做)
在不想儲存分數的情況下,整型(在C/C++語系中是int,以及short、long和現在的long long)是比較好的選擇———但我們這裡不談小數。
不過,有時候整數在位於一個長表示式中時也會牽扯到小數。下面就是我的書《深入淺出C++(第2版)》(C++ Without Fear)中的一行程式碼:
cout << results / (n / 10);
這個程式用於生成0到9之間的隨機數,每個數字出現的概率應該是1/10。這裡是用每個數字實際出現的次數除以期望它出現的次數。比如,如果3的results(出現的實際次數)是997,而總共進行了10 000次測試,那麼就是用997除以期望它出現的次數:1000。
此時的results、n和10都是整數。因此,997除以1000後得到的是0!
……等等,我們覺得應該得0.997啊。怎麼回事?
整數除法向下舍入,於是會得到一個完美的整數。餘數呢?扔了。好像也不是完全沒有道理。C++提供了兩種 獨立的除法操作:除和求餘。
int dividend = n / m; //儲存比例
int remainder = n % m; //儲存餘數
噢——對了,前面那行程式碼中使用了一個“魔法數字”——10,我暈~$#@&^!好了,下一條要訣我們就聊聊這個更麻煩的事兒:資料丟失。
4:記住利用資料提升來控制結果
在混有整數和浮點數的表示式中,整數運算數會被提升為double型。因此,下面的表示式可以得到我們想要的結果:
cout << results / (n / 10.0);
注意,雖然並沒有小數,但這裡用了10.0,就是為了把它儲存為double型。這樣,C++就會把n和results也提升為double型,最終達到執行浮點除法的目的:
另一種達到目的的方法是轉型資料:
cout << results / (n / (double) 10);
cout << results / (n / static_cast<double>(10));
5:不要使用非布林條件(除非你會留意)
C語言最初是為了編寫作業系統而設計的,因而它賦予了程式設計師很大的自由,不僅可以在機器碼的層面上運算元據(通過使用指標),還可以寫出快捷方法。快捷方法對於初學者是很危險的,但對於知道該怎麼用的程式設計師來說,有時候則非常可心。但願他們心甘情願活在危險之中。
比如,下面是“做N次……”的一種最優雅的快捷寫法:
while (n--){
do_something();
}
實際上還可以寫得更短:
while (n--) do_something();
同樣,無論怎樣寫,都是基於一條相同的規則:C和C++中的任何數值都可以作為條件來用。當每次迴圈遞減1,每次迴圈遞減1,直到n變成0時,迴圈結束。不過,這裡有問題:如果n一開始就是負值呢?那剛才看到的那個迴圈就會永遠迴圈下去,至少是可以迴圈到記憶體溢位之前的最小負值。這會破壞你的好心情的。
一般來說,只有嚴格意義上的Boolean(也就是true或false)表示式才能用作條件:
while (n-- > 0){
do_something();
}
不過也有一個例外。當某個操作失敗時,將指標設定為NULL(也就是0),那這個簡寫方式就能起作用。實際上,NULL意味著false。在連結串列結構中,空指標也可以用來表示列表的結束,就是讓它的next_node成員指向不存在的地方(nowhere;1950年和1960年的時候,說是指向Nowheresville)。在下面這種情況下,空指標意味著開啟檔案失敗:
if (! file_pointer){
cout << "File open failed.";
return ERROR_CODE;
}
提醒一句,你應該在這個條件中使用一個賦值語句:
if (! (file_pointer = open_the_file(args)){
cout << "File open failed.";
return ERROR_CODE;
}
(To be continued!)
相關文章
- 正確編寫C++程式碼的十大要訣(1)C++
- 正確編寫C++程式碼的十大要訣(2)C++
- 編寫可移植C/C++程式的要點C++
- EA指令碼編寫要點指令碼
- 正確編寫Designated Initializer的幾個原則
- 編寫優秀程式碼的10個技巧
- 如何編寫更棒的程式碼:11個核心要點
- C++編譯錯誤的正確查詢方式C++編譯
- Java 正確的做字串編碼轉換Java字串編碼
- 編寫"谷歌擴充套件"便捷"自測"埋點上報資訊是否正確谷歌套件
- 正確進行程式碼優化,一點思想論行程優化
- 編寫更好程式碼的 6 個提示
- 編寫好程式碼的10條戒律
- 編寫高質量的js之正確理解正規表示式回溯JS
- Laravel 單元測試實戰(3)- 編寫整合測試確保介面和資料庫程式碼正確Laravel資料庫
- 編寫更好程式碼的6個提示【已翻譯100%】
- C++編寫pingIP的程式C++
- 編寫易維護跨端元件的正確姿勢跨端元件
- 編寫Spark程式的幾個最佳化點Spark
- 程式設計師正確看程式碼的方式程式設計師
- 五顏六色的程式碼:論程式碼的正確位置
- 正確理解PHP程式編譯時的錯誤資訊PHP編譯
- Linux編寫Bash指令碼的10個技巧Linux指令碼
- 為什麼 Python 程式碼要寫得美觀而明確Python
- Git Bash 提交程式碼的正確姿勢Git
- DDD建模後寫程式碼的正確姿勢(Java、dotnet雙平臺)Java
- 編寫優秀 CSS 程式碼的 8 個策略CSS
- 編寫出色CSS程式碼的13個建議CSS
- 要點提煉| 理解JVM之程式編譯&程式碼優化JVM編譯優化
- 寫好程式碼的10個祕密
- 能讓你少寫1000行程式碼的20個正規表示式行程
- 單例模式的正確寫法單例模式
- 你寫的 return null 正確嗎?Null
- 寫一個 iOS 複雜表單的正確姿勢iOS
- java安全編碼指南之:lock和同步的正確使用Java
- 成為偉大程式設計師的 10 個要點程式設計師
- 格局為上,寫程式碼之餘要懂點兒SDN
- 編寫高質量程式碼的十個祕訣