C++小知識:為什麼錯誤程式碼能正常工作
本篇案例來自 Miranda NG 原始碼。
錯誤程式碼:
#define MF_BYCOMMAND 0x00000000L
void CMenuBar::updateState(const HMENU hMenu) const
{
....
::CheckMenuItem(hMenu, ID_VIEW_SHOWAVATAR,
MF_BYCOMMAND | dat->bShowAvatar ? MF_CHECKED : MF_UNCHECKED);
....
}
問題解釋:
我想提出一個發人深思的主題來討論:有時候,我們會看到完全不正確的程式碼卻能工作得很好!
經驗豐富的程式猿不會對這個問題感到驚訝(這又是另一個故事),但對那些最近剛開始學習 C/C++ 的人來說,這個問題也許有點讓人困惑。那麼,今天我們就來看看這樣一個示例。
在上面顯示的程式碼中,必須在呼叫 CheckMenuItem() 時設定標誌位;然後,先看 bShowAvatar 是否為 true,如果成立MF_BYCOMMAND 就和 MF_CHECKED 按位或運算,反之則和 MF_UNCHECKED 做按位或運算。就這麼簡單!
MF_BYCOMMAND | dat->bShowAvatar ? MF_CHECKED : MF_UNCHECKED
在上面的程式碼中,這位程式猿很自然地選擇三元運算子來表示這個過程(這個運算子是 if-then-else 的簡便版):
MF_BYCOMMAND | dat->bShowAvatar ? MF_CHECKED : MF_UNCHECKED
問題是,按位或運算子 | 比運算子 ?: 的優先順序更高(詳細看 C/C++ 運算優先順序)。很明顯這裡有兩個錯誤。
第一個錯誤是條件變了,不再是“dat->bShowAvatar”,而變成了“MF_BYCOMMAND | dat->bShowAvatar”。
第二個錯誤是隻選擇了一個標誌位 —— MF_CHECKED 或 MF_UNCHECKED,標誌位 MF_BYCOMMAND 不見了。
儘管存在這些錯誤,這條程式碼仍然能正常工作!這純粹是因為運氣。這位程式猿很走運,因為MF_BYCOMMAND 等於 0x0000000L。
當標誌位 MF_BYCOMMAND 等於 0,也不會對程式碼有任何影響。可能某些經驗豐富的程式猿已然明瞭,但萬一這裡有新手,我還是詳細解釋一下其中的緣由。
先來看看加了括號的正確表示式:
MF_BYCOMMAND | (dat->bShowAvatar ? MF_CHECKED : MF_UNCHECKED)
用數值替換巨集:
0x00000000L | (dat->bShowAvatar ? 0x00000008L : 0x00000000L)
如果運算子“|”兩邊的運算元之一為0,那我們可以將表示式簡化為:
dat->bShowAvatar ? 0x00000008L : 0x00000000L
現在來進一步看看錯誤程式碼的變體:
MF_BYCOMMAND | dat->bShowAvatar ? MF_CHECKED : MF_UNCHECKED
用數值代替巨集:
0x00000000L | dat->bShowAvatar ? 0x00000008L : 0x00000000L
在子表示式“0x0000000L | dat->bShowAvatar”中,運算子 | 的運算元之一為0,將表示式簡化為:
dat->bShowAvatar ? 0x00000008L : 0x00000000L
結果就是,我們得到了相同的表示式,這就是錯誤程式碼能正確工作的原因。又發生了一個程式設計奇蹟。
正確程式碼這麼寫:
有很多種方法可以修正這段程式碼,其中一個方法就是加上圓括號,另外一個就是增加一箇中間變數。用大家熟悉的操作符“if”在這裡也會有幫助:
if (dat->bShowAvatar)
::CheckMenuItem(hMenu, ID_VIEW_SHOWAVATAR,MF_BYCOMMAND | MF_CHECKED);
else
::CheckMenuItem(hMenu, ID_VIEW_SHOWAVATAR,MF_BYCOMMAND | MF_UNCHECKED);
我真的不是強調非用這種方式修正程式碼不可。這樣也許更易閱讀,但稍微有點長,所以這更多的是個人偏好。
建議:
我的建議很簡單 —— 設法避免複雜的表示式,尤其是在使用三元運算子的時候。還有,別忘了圓括號。
要記住,操作符 “?:”很危險!有時你會忘記它的優先順序比較低,並且容易寫出錯誤的表示式。人們常常在塞滿一個字串時使用這個操作符,因此儘量不要那麼做。還有什麼問題或者想一起交流的可以加這個群:941636044 ,群裡面也有一些方便學習C++的資料。