《C 覆轍錄》 1 11 聰明反被聰明誤

2021-09-23 16:23:25 字數 2093 閱讀 2267

c++語言和c語言看起來會吸引相當多的人去張揚個性(你有沒有聽說過乙個叫「obfuscated eiffel」的比賽?)46。在這些軟體工程師的思維裡,兩點間的最短距離是普通歐氏空間之球面扭曲上的大圓。

試舉一例:在c++語言的圈子裡(且不論這個圈子是不是普通歐氏空間裡的),**的排版格式純粹是為了方便解讀**的人類的,而對於**47的意義,只要語彙塊的次序還是按原先的次序的依次出現,就怎麼都無所謂。這最後乙個附加條款殊為重要,比如,以下這兩行表示的是非常不同的意思48(但是請看常見錯誤87):

a+++++b; // 錯誤!④

a+++ ++b; // 沒問題```

④譯者注:本行等價於`a++ ++ + b`,而`a++`不是乙個左值。

以下兩行也是同出一轍(參見常見錯誤17):

ptr->*m; // 沒問題

ptr-> *m; // 錯誤!⑤`

⑤譯者注:->*合起來才是乙個運算子。

上面的例子容易讓大多數c++軟體工程師同意,只要注意不去趟語彙塊劃分錯誤的渾水,**的排版格式就再次高枕無憂地和**的意義無關了。因此,把乙個宣告變數的語句寫在一行裡還是分成兩行寫,結果別無二致。(有一些軟體開發環境的偵錯程式以及其他工具元件是依據**的行數,而不是其他更精確的定位邏輯來實現的。這樣的工具經常強迫軟體工程師去把本來可以寫在一行裡的**硬分成既不自然也不方便的數行來寫,以得到更精準的錯誤提示錯誤,或是設定更精準的斷點,等等。這不是c++語言的毛病,而是c++軟體開發環境作者的毛病。)

long curline =  line   ; // 取得當前行數值

long curline

=   line

; // 同樣的宣告⑥```

⑥譯者注:但是,結果變得毫無意義了。同樣的建議參見(kernighan, 2002),§1。

絕大多數的c++軟體工程師在這一點上都會犯錯。讓我們看乙個平凡的用模板元程式設計實現的可以在編譯期遴選一種型別的設施:

template

struct select ;

template

struct select ;`

具現select模板的過程是先在編譯期對乙個條件評估求值,然後根據此表示式的布林結果具現此模板的兩個版本之一。這相當於乙個編譯期的if語句說「如果條件為真,那麼內含的result的型別就是a,否則它的型別就是b。」

select< sizeof(int)==sizeof(long), int, long >::result temp = 0;```

上面這個語句宣告了乙個變數`temp`,如果在某特定平台上int型別和long型別占用的位元組數是一樣的,那麼變數的`temp`的型別就是int,否則它的型別就是`ong`。

再讓我們看看前面宣告的那個`curline`吧。我們幹嘛沒事找事地寫浪費那麼多空格的空間呢?不過權且讓我們沒什麼理由地把問題複雜化好了:

const char cm = char_max;

const select<_ line _<=cm,char,long>::result curline =_ _line ;`

上面這段**是管用的(且算它正確),但是這一行太長了,所以維護工程師便隨後稍稍把它重新排了一下版:

const select<   line   <=cm,char,long>::result

curline =_ _line_ _; ```

現在我們的**裡有了乙個bug,你看出來了嗎?

在**行數為`char_max`(它可能小到只有127)的那一行裡,以上的宣告會導致什麼結果?

`curline`的型別會被宣告為`char`,並被初始化為`char`型別的最大值。隨著我們把初始化源放到了下一行,我們就會把`curline`的值初始化為char型別的最大值還要大1的數。這個結果很可能會指出,當前行數是乙個負數(比如−128)49。多麼聰明啊!

聰明反被聰明誤在c++軟體工程師身上算乙個常見的問題。請時刻牢記,幾乎在所有的場合下,遵循習慣用法、清晰的表達和一點點效率的折損都好過小聰明、模稜兩可和維護便利的喪失。

《C 覆轍錄》 2 6 宣告飾詞次序的小聰明

就語言本身所限,宣告飾詞孰先孰後純屬無關緊要的形而上之爭 int const extern size 1024 合法,但有離奇不經之嫌 無論如何,如果沒有令人信服的理由去背離習慣用法,那麼頂好還是接受有關宣告飾詞次序事實上的標準 先寫連線飾詞,再寫量化飾詞,再寫型別。extern const int...

《C 覆轍錄》 1 3 全域性變數

很難找到任何理由去硬生生地宣告什麼全域性變數。全域性變數阻礙了 重用,而且使 變得更難維護。它們阻礙重用是因為任何使用了全域性變數的 就立刻與之耦合,這使得全域性變數一改它們也非得跟著改,從而使任何重用都不可能了。它們使 變得更難維護的原因是很難甄別出哪些 用了某個特定的全域性變數,因為任何 都有訪...

《C 覆轍錄》 常見錯誤1 過分積極的注釋

c 覆轍錄 說乙個問題是基礎的,並不就是說它不是嚴重的或不是普遍存在的。事實上,本章所討論的基礎問題的共同特點比起在以後章節討論的技術複雜度而言,可能更側重於使人警醒。這裡討論的問題,由於它們的基礎性,在某種程度上可以說它們普遍存在於幾乎所有的c 中。很多注釋都是畫蛇添足,它們只會讓源 更難讀,更難...