您的位置:首頁>科技>正文

C語言程式設計過程中的一些建議

過程名稱

過程名稱應該表明它們是做什麼的, 函數名稱應該表明它們返回什麼。 函數通常在像 if 這樣的運算式使用, 因此可讀性要好。

C/C++從入門到大牛 Ⅱ369203660

注釋

這一個微妙的問題, 需要自己體會和判斷。 由於一些原因, 我傾向于寧可清除注釋。 第一, 假如代碼清晰, 並且使用了規範的類型名稱和變數名稱, 應該從代碼本身就可以理解。 第二, 編譯器不能檢查注釋, 因此不能保證準確, 特別是代碼修改過以後。 誤導性的注釋會非常令人困惑。 第三, 排版問題:注釋會使代碼變得雜亂。

但有時我會寫注釋, 像下文一樣僅僅只是把它們用於介紹。

例如:解釋全域變數的使用和類型(我總是在龐大的程式中寫注釋);作為一個不尋常或者關鍵過程的介紹;或標記出大規模計算的一節。

有一個糟糕注釋風格的例子:

C/C++從入門到大牛 Ⅱ369203660

先不要嘲笑, 等到在現實中看到再去吧。

或許除了諸如重要資料結構的聲明(對資料的注釋通常比對演算法的更有説明), 這樣至關重要部分之外, 需要避免對注釋的“可愛”排版和大段的注釋;基本上最好就不要寫注釋。 如果代碼需要靠注釋來說明, 那最好的方法是重寫代碼, 以便能更容易地理解。 這就把我們帶到了複雜度。

複雜度

許多程式過於複雜, 比需要有效解決的問題更加複雜。 這是為什麼呢?大部分是由於設計不好,

但我會跳過這個問題, 因為這個問題太大了。 然而程式往往在微觀層面就很複雜, 有關這些可以在這裡解決。

規則 1:不要斷定程式會在什麼地方耗費執行時間。

瓶頸總是出現在令人意想不到的地方, 直到證實瓶頸在哪, 不要試圖再次猜測並加快運行速度。

規則 2:估量(measure)

在沒有對代碼做出估量之前不要優化速度, 除非發現最耗時的那部分代碼, 要不也不要去做。

規則 3:當 n 很小時(通常也很小), 花哨的演算法運行很慢。

花哨演算法有很大的常數級別複雜度。 在你確定 n 總是很大之前, 不要使用花哨演算法。 (即使假如 n 變大, 也優先使用規則 2).例如, 對於常見問題, 二叉樹總比伸展樹高效。

規則 4:花哨的演算法比簡單的演算法更容易有 bug,

而且實現起來也更困難

儘量使用簡單的演算法與簡單的資料結構。

以下幾乎是所有實際程式中用到的資料結構:

陣列

鏈表

雜湊表

二叉樹

當然也必須要有把這些資料結構靈活結合的準備, 比如用雜湊表實現的符號表, 其中雜湊表是由字元型陣列組成的鏈表。

規則 5:以資料為核心

如果選擇了適當的資料結構並把一切都組織得很有條理性, 演算法總是不言而喻的。 程式設計的核心是資料結構, 而不是演算法。 (參考 Brooks p. 102)

規則 6:就是沒有規則 6。

資料程式設計

不像許多 if 語句, 演算法或演算法的細節通常以緊湊、高效和明確的資料進行編碼。 眼前的工作可以編碼, 歸根到底是由於其複雜性都是由不相干的細節組合而成。

分析表是典型例子, 它通過一種解析固定、簡單程式碼片段的形式, 對程式設計語言的語法進行編碼。 有限狀態機特別適合這種處理形式, 但是幾乎任何涉及到對構建資料驅動演算法有益的程式, 都是將某些抽象資料類型的輸入“解析”成序列, 序列會由一些獨立“動作”構成。

也許這種設計最有趣的地方是表結構有時可以由另一個程式生成(經典案例是解析生成器)。 有個更接地氣的例子, 假如作業系統是由一組表驅動, 這組表包含連接 I/O 請求到相應設備驅動的操作, 那麼可以通過程式“配置“系統, 該程式可以讀取到某些特殊設備與可疑機器連接的描述, 並列印相應的表。

資料驅動程式在初學者中不常見的原因之一是由於 Pascal 的專制。

Pascal 像它的創始人一樣, 堅信代碼要和資料分開。 因而(至少在原始形式上)無法創建初始化的資料。 與圖靈和馮諾依曼的理論背道而馳, 這些理論可都是定義存儲電腦的基本原理。 代碼和資料是一樣的, 或至少可以算是。 還能怎樣解釋編譯器的工作原理呢?(函數式語言對 I/O 也有類似的問題)

函數指標

Pascal 專制的另一個結果是初學者不使用函數指標。 (在 Pascal 中沒有把函數作為變數) 用函數指標來處理編碼複雜度會有一些令人感興趣的地方。

指標指向的程式有一定的複雜度。 這些程式必須遵守一些標準協定, 像要求一組都是相同調用的程式就是其中之一。 除此之外, 所要實現的只是完成業務, 複雜度是分散的。

有個協定的主張是既然所有使用的功能相似,那麼它們的行為也必須相似。這對簡單的文檔、測試、程式擴展和甚至使程式通過網路分佈都有説明——遠端程序呼叫可以通過該協定進行編碼。

我認為面相物件程式設計的核心是清晰使用函數指標。規定好要對資料執行的一系列操作,以及對這些操作回應的整套資料類型。將程式合攏到一起最簡單的方法是為每種類型使用一組函數指標。簡而言之,就是定義類和方法。當然,物件導向語言提供了更多更漂亮的語法、派生類型等等,但在概念上幾乎沒有提出額外的東西。

資料驅動程式與函數指標的結合,變成了一種表現令人驚訝的工作方法。根據我的經驗,這種方法經常會產生驚喜的結果。即使沒有物件導向語言,無需額外的工作也可以獲得 90% 的好處,並且能更好地管理結果。我無法再推薦出更高標準的實現方式。我所有的程式都是由這種方式組織管理,而且經過多次開發後都相安無事——遠遠優於缺少約束的方法。也許正如所說:從長遠來看,約束會帶來豐厚的回報。大家參加如需學習交流請加群C語言C++學習交流369203660

包含檔

簡單規則:包含(include)檔時應該永遠不要嵌套包含。

如果聲明(在注釋或隱式聲明裡)需要的檔沒有優先包含進來,那麼使用者(程式師)要決定包含哪些檔,但要以簡單的方式處理,並採用避免多重包含的結構。多重包含是系統程式設計的禍根。將檔包含五次或更多次來編譯一個單獨的 C 原始檔案的事情屢見不鮮。Unix 系統中 /usr/include/sys 就用了這麼可怕的方式。

說到 #ifdef,有一個小插曲,雖然它能防止讀取兩次檔,但實際上經常用錯。#ifdef 是定義在檔本身中,而不是檔包含它。結果是常常導致讓成千上萬不必要的代碼通過詞彙分析器,這是(優秀編譯器中)耗費最大的階段。

只需遵從以上簡單規則。C/C++從入門到大牛 Ⅱ369203660

請養成良好的閱讀習慣,看完如果覺得喜歡的話請關注轉發評論收藏一下 感謝!

複雜度是分散的。

有個協定的主張是既然所有使用的功能相似,那麼它們的行為也必須相似。這對簡單的文檔、測試、程式擴展和甚至使程式通過網路分佈都有説明——遠端程序呼叫可以通過該協定進行編碼。

我認為面相物件程式設計的核心是清晰使用函數指標。規定好要對資料執行的一系列操作,以及對這些操作回應的整套資料類型。將程式合攏到一起最簡單的方法是為每種類型使用一組函數指標。簡而言之,就是定義類和方法。當然,物件導向語言提供了更多更漂亮的語法、派生類型等等,但在概念上幾乎沒有提出額外的東西。

資料驅動程式與函數指標的結合,變成了一種表現令人驚訝的工作方法。根據我的經驗,這種方法經常會產生驚喜的結果。即使沒有物件導向語言,無需額外的工作也可以獲得 90% 的好處,並且能更好地管理結果。我無法再推薦出更高標準的實現方式。我所有的程式都是由這種方式組織管理,而且經過多次開發後都相安無事——遠遠優於缺少約束的方法。也許正如所說:從長遠來看,約束會帶來豐厚的回報。大家參加如需學習交流請加群C語言C++學習交流369203660

包含檔

簡單規則:包含(include)檔時應該永遠不要嵌套包含。

如果聲明(在注釋或隱式聲明裡)需要的檔沒有優先包含進來,那麼使用者(程式師)要決定包含哪些檔,但要以簡單的方式處理,並採用避免多重包含的結構。多重包含是系統程式設計的禍根。將檔包含五次或更多次來編譯一個單獨的 C 原始檔案的事情屢見不鮮。Unix 系統中 /usr/include/sys 就用了這麼可怕的方式。

說到 #ifdef,有一個小插曲,雖然它能防止讀取兩次檔,但實際上經常用錯。#ifdef 是定義在檔本身中,而不是檔包含它。結果是常常導致讓成千上萬不必要的代碼通過詞彙分析器,這是(優秀編譯器中)耗費最大的階段。

只需遵從以上簡單規則。C/C++從入門到大牛 Ⅱ369203660

請養成良好的閱讀習慣,看完如果覺得喜歡的話請關注轉發評論收藏一下 感謝!

Next Article
喜欢就按个赞吧!!!
点击关闭提示