每天學點C++知識:不要去做編譯器的工作

黃小非發表於2016-01-16

對於C++程式設計的老鳥來說,有時候他們喜歡把一些東西按照編譯器的工作原理進行改寫,以便提高程式碼的執行效率。這麼做確實高明,也能體現出程式設計師的水平,但是這麼做也是有風險的。因為有時候你可能會因為一些簡單的筆誤,而造成非常難以察覺的錯誤。本文就給出了類似的例子。

這個 Bug 出現在 MySQL 原始碼中。

錯誤程式碼:

說明:

這是一個在對程式碼段進行拷貝貼上時出現的典型錯誤。程式設計師很可能是把“if (a[1] != b[1]) (int) a[1] – (int) b[1];” 這段程式碼拷貝了好幾遍(然後手動改陣列下標),用來實現一個迴圈。不過程式設計師忘記把其中某一行的陣列下標 1 改成 5。結果就是函式有時候能返回正確的值(,有的時候則不行),這種錯誤是很難偵測的。事實上這個錯誤的確很難捕捉,在我們用 PVS-Studio 掃描 MySQL 原始碼之前,所有其他的測試都沒能發現這個錯誤。

正確的程式碼:

儘管之前的程式碼看上去整潔易讀,但是程式設計師還是很有可能漏看這個錯誤。因為這個程式碼塊的內部結構很相似,所以你本能地會一掃而過,而不會特別集中注意力去閱讀程式碼。

之所以把程式碼寫成這樣,很可能是程式設計師想盡可能地優化程式碼。他(或她)想手動“展開迴圈”(來進行優化)。不過我想在這兒可不是個好主意。

首先,我很懷疑程式設計師是不是真的能通過這種方法達到效果。要知道,現代編譯器已經相當智慧了,如果真的能優化程式效能,(編譯器)自動就會完成展開迴圈的優化。

其次,由於嘗試進行優化卻造成了程式碼中出現 bug。如果程式設計師一開始能老老實實寫一個簡單迴圈,那麼犯錯誤的機率就會降低很多。

我建議把這個方法寫成這樣:

這種寫法有兩個優勢:

  • 這個函式更容易閱讀和理解。
  • 編寫程式碼時,降低犯錯機率。

至於效能方面,我敢說這個版本不會比之前寫得很長的那個版本慢。

這個推薦的方法實際上表達了下面的意思:程式碼要簡單易讀。簡單的程式碼通常即是正確的程式碼。不要去做編譯器的工作——例如,(手動)展開迴圈。編譯器很明確知道自己該做什麼,並不需要你的幫助。手動程式碼優化工作只針對某些特定的關鍵程式碼,而且只在分析器已經確認這些程式碼是瓶頸以後,才可能恰當。

這個錯誤由 PVS-Studio 分析工具捕獲。錯誤文字:V525程式碼包含一組相似的程式碼塊。

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

每天學點C++知識:不要去做編譯器的工作

相關文章