前言
你拿起這本書是為了提升你的編程技能。很好,因為你一定會從這本書提供的實踐知識中受益。如果你有豐富的C 語言編程經驗,你將學習到良好設計決策的細節(jié),以及它們的優(yōu)缺點。如果你對C 語言編程還比較陌生,你會找到關于設計決策的指導,并且你會看到這些決策如何一點一點地應用到運行的代碼示例中,以構建更大規(guī)模的程序。
這本書回答了如何構建C 程序、如何應對錯誤處理、如何設計靈活接口等問題。
當你對C 語言編程了解得更多時,經常會出現(xiàn)以下問題:
? 我應該返回任何錯誤信息嗎?
? 我應該使用全局變量errno 來做這件事嗎?
? 我應該有幾個參數(shù)很多的函數(shù)還是相反?
? 我如何構建一個靈活的接口?
? 我如何構建基本的東西,比如迭代器?
對于面向對象語言,這些問題大多數(shù)在《設計模式:可復用面向對象軟件的基礎》一書中得到了很大程度的解答,該書由Erich Gamma、Richard Helm、Ralph Johnson 和John Vlissides 編著(Prentice Hall,1997)。設計模式為程序員提供了有關對象如何交互以及哪個對象擁有哪種其他對象的最佳實踐。
此外,設計模式展示了這些對象如何組合在一起。
然而,對于像C 這樣的過程編程語言,大多數(shù)這些設計模式無法按照四人組描述的方式實現(xiàn)。C 中沒有原生的面向對象機制。雖然可以在C 編程語言中模擬繼承或多態(tài),但這可能不是首選,對于那些使用C 編程且不熟悉面向對象語言(如C )以及繼承和多態(tài)等概念的程序員,可能更愿意堅持他們熟悉的原生C 編程風格。然而,采用原生C 編程風格,并不是所有面向對象設計模式的指導都適用,或者至少對于非面向對象編程語言,無法提供設計模式中所呈現(xiàn)的特定實現(xiàn)思路。
我們所面臨的情況是:我們想在C 中進行編程,但不能直接應用設計模式中的大部分知識。本書展示了如何填補這個差距,將實際的設計知識應用于C編程語言中。
我為什么寫這本書?
讓我來告訴你為什么本書中所匯集的知識對我來說非常重要,以及為什么這樣的知識很難找到。
在學校里,我學習了C 編程作為我的第一門編程語言。就像每個新的C 程序員一樣,我想知道為什么數(shù)組索引從0 開始,我一開始甚至隨機嘗試如何放置運算符* 和&,來弄懂C 指針里的魔法到底如何起作用的。
在大學里,我學習了C 語法的實際工作原理以及它如何在硬件上轉化為位和字節(jié)。有了這些知識,我能夠編寫出表現(xiàn)非常好的小程序。然而,我仍然很難理解為什么較長的代碼看起來是這個樣子,我肯定永遠不會再提出像以下這樣的解決方案:
typedef struct INTERNAL_DRIVER_STRUCT* DRIVER_HANDLE;
typedef void (*DriverSend_FP)(char byte);
typedef char (*DriverReceive_FP)();
typedef void (*DriverIOCTL_FP)(int ioctl, void* context);
struct DriverFunctions
{
DriverSend_FP fpSend;
DriverReceive_FP fpReceive;
DriverIOCTL_FP fpIOCTL;
};
DRIVER_HANDLE driverCreate(void* initArg, struct DriverFunctions f);
void driverDestroy(DRIVER_HANDLE h);
void sendByte(DRIVER_HANDLE h, char byte);
char receiveByte(DRIVER_HANDLE h);
void driverIOCTL(DRIVER_HANDLE h, int ioctl, void* context);
這樣的代碼引會發(fā)出許多問題:
? 為什么在結構體中需要函數(shù)指針?
? 為什么函數(shù)需要那個DRIVER_HANDLE ?
? IOCTL 是什么,為什么我不能使用單獨的函數(shù)?
? 為什么必須顯式的創(chuàng)建和銷毀函數(shù)?
當我開始編寫產品級的應用程序時,這些問題就浮現(xiàn)出來了。我經常遇到一些情況,讓我意識到我沒有足夠的C 編程知識,例如,我無法決定如何實現(xiàn)迭代器,或者決定如何在我的函數(shù)中進行錯誤處理。我意識到,盡管我了解C 語法,但我不知道如何應用它。我試圖做一些事情,但只是以笨拙的方式或根本無法實現(xiàn)。我需要的是關于如何使用C 編程語言實現(xiàn)特定任務的最佳實踐。例如,我需要知道類似以下的事情:
? 我如何以簡單的方式獲取和釋放資源?
? 在錯誤處理中使用goto 是個好主意嗎?
? 我是應該將接口設計得靈活,還是在需要時直接更改它?
? 我是應該使用assert 語句,還是應該返回錯誤代碼?
? 在C 中如何實現(xiàn)迭代器?
對我來說,非常有趣的一點是,雖然我的有經驗的同事們對這些問題有很多不同的答案,但沒有人告訴我哪里有了解這些設計決策及其利弊的文檔。
所以接下來我轉向了互聯(lián)網(wǎng),然而我再次感到驚訝:盡管C 編程語言已經存在了幾十年,但很難找到這些問題的答案。我發(fā)現(xiàn),雖然有很多關于C 編程語言基礎和語法的文獻,但很少涉及高級C 編程主題,或者告訴人們如何編寫能夠應對產品級應用的優(yōu)美C 代碼。
而這正是這本書的用武之地。本書教會你如何提升從編寫基本的C 程序,轉而到編寫考慮錯誤處理的大規(guī)模C 程序,并對未來的需求和設計變化保持靈活的編程技能。本書使用設計模式的概念,逐步為您提供設計決策及其利弊。這些設計模式被應用于運行的代碼示例,教會你為什么先前的示例代碼會演變成現(xiàn)在的樣子。
這些呈現(xiàn)的模式可以應用于任何C 編程領域。由于我來自多線程實時環(huán)境的嵌入式編程領域,因此一些模式可能會偏向于該領域。不管怎樣,你將會看到這些模式的通用思想可以應用于其他C編程領域,甚至超越了C編程的范圍。
模式基礎
本書中的設計指導以模式的形式呈現(xiàn)。將知識和最佳實踐以模式的形式呈現(xiàn)的想法源自建筑師克里斯托弗·亞歷山大的《建筑的永恒之道》(牛津大學出版社,1979)。他使用經過驗證的小型解決方案來解決他所在領域的一個巨大問題:如何設計和建造城市。這種應用模式的方法被軟件開發(fā)領域采納,其中舉辦了一些關于模式的會議,如模式語言程序會議(PLoP)等,來用于擴展關于模式的知識體系。特別是由四人組(Gang of Four)《設計模式:可復用面向對象軟件的基礎》一書(Prentice Hall,1997),對軟件開發(fā)人員產生了重要影響,使設計模式的概念為軟件開發(fā)人員廣為人知。
但是,什么是模式?市面上有很多定義,如果您對此主題非常感興趣,那么弗蘭克·布施曼(Frank Buschmann)等編寫的《面向模式的軟件架構:模式和模式語言》(Wiley,2007)一書可以為你提供準確的描述和細節(jié)。在本書中,模式為實際問題提供了經過驗證的解決方案。本書中呈現(xiàn)的模式具有的結構如表1 所示。
表1:本書中呈現(xiàn)的模式
模式部分 說明
名字 這是模式的名稱,應該容易記住。目標是讓程序員在日常語言中使用這個名稱(就像四人組模式一樣,程序員會說:抽象工廠創(chuàng)建對象)。本書中的模式名稱以大寫字母書寫
上下文 上下文部分為模式設置了背景。它告訴你在哪些情況下可以應用這種模式問題 問題部分向您提供了你想要解決的問題的信息。它以粗體字體開始,列出主要的問題陳述,然后詳細說明為什么這個問題很難解決(在其他模式格式中,這些詳細信息放在一個稱為forces的單獨部分中)
解決方案 這一部分提供了如何解決問題的指導。它以粗體字體陳述解決方案的主要思想,然后繼續(xù)詳細說明解決方案。它還通過提供代碼示例來提供非常具體的指導結果 這一部分列出了應用所描述解決方案的利弊。在應用模式時,你應該始終確認產生的結果是否符合預期
已知用例 已知用例提供了所提出的解決方案在實際應用中是有效的證據(jù)。它們還向你展示具體的示例,幫助你理解如何應用該模式
以模式的形式呈現(xiàn)設計指導的一個重要優(yōu)勢是這些模式可以應用在各個場景。如果你面臨的是一個巨大的設計問題,將很難找到一個確切的指導文檔或解決方案,可以恰好解決這個問題。相反,你可以將巨大且特定的問題視為許多較小和更通用的問題的總和,并通過逐個應用模式來逐步解決這些問題。你只需檢查模式所對應的問題描述,選擇并應用適合你的問題的模式,并得到一些結果。這些模式應用的結果可能會導致另一個問題,你可以通過接著應用另一個模式來解決。通過這種方式,逐步地設計你的代碼,而不是試圖在編寫第一行代碼之前提前產出一個完整的設計。
如何閱讀這本書?
你應該對C 語言的基礎知識有了一些了解。你應該知道C 語言的語法以及它是如何工作的。例如,這本書不會教你什么是指針或如何使用它。這本書旨在對于一些高級抽象地話題提供提示和指南。
本書中的章節(jié)是獨立的。你可以按任意順序閱讀它們,并且你可以簡單地挑選出你感興趣的話題。在接下來的一段,你會找到所有模式的概覽,從那里你可以跳轉到你感興趣的模式。所以,如果你確切地知道你在尋找什么,直接開始就好了。
如果你不是在尋找某一個特定的模式,而是想要對可能的C 程序的設計選項有一個概覽,請通讀本書。在本書中,每章都關注一個特定的話題,從如錯誤處理和內存管理等基本話題開始,然后會轉移到更高級和特定的話題,如接口設計或平臺獨立代碼。在每章中,都會介紹與該話題相關的模式,以及逐步展示如何應用這些模式運行代碼示例。
本書的部分展示了兩個較大的運行示例,在這兩個例子里應用了許多的模式。
在這里,你可以學習如何通過應用模式逐步構建一些更加大型的軟件。
OReilly 在線學習平臺(OReilly Online Learning)
近40 年來,OReilly Media 致力于提供技術和商業(yè)培訓、知識和卓越見解,來幫助眾多公司取得成功。
公司獨有的專家和改革創(chuàng)新者網(wǎng)絡通過OReilly 書籍、文章以及在線學習平臺,分享他們的專業(yè)知識和實踐經驗。OReilly 在線學習平臺按照您的需要提供實時培訓課程、深入學習渠道、交互式編程環(huán)境以及來自OReilly 和其他200 多家出版商的大量書籍與視頻資料。更多信息,請訪問網(wǎng)站:https://www.oreilly.com/。
聯(lián)系我們
任何有關本書的意見或疑問,請按照以下地址聯(lián)系出版社。
美國:
OReilly Media, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
中國:
北京市西城區(qū)西直門南大街2 號成銘大廈C 座807 室(100035)
奧萊利技術咨詢(北京)有限公司
我們?yōu)楸緯渲昧藢倬W(wǎng)頁,在上面列出了勘誤表、示例以及其他附加信息。
你可以通過https://oreil.ly/fluent-c 來訪問。
如果你對本書有一些評論或技術上的建議,請發(fā)送電子郵件到errata@oreilly.com.cn。
要了解OReilly 的圖書和培訓課程的新聞和信息,請訪問我們的網(wǎng)站https://oreilly.com。
我們的LinkedIn:https://linkedin.com/company/oreilly-media。
我們的Twitter:https://twitter.com/oreillymedia。
我們的YouTube:https://www.youtube.com/oreillymedia。
致謝
我想感謝我的妻子Silke,到現(xiàn)在為止她甚至知道什么是模式,我也想感謝我的女兒Ylvi。她們兩個都讓我的生活更加快樂,確保我不總是坐在電腦前工作,而是享受生活。
沒有許多模式愛好者的幫助,這本書不可能面世。我想感謝所有在歐洲程序模式語言會議上的作家工作坊的參與者為我提供關于模式的反饋。特別是,我想感謝以下人員,在會議的所謂的牧羊人過程中為我提供了非常有用的反饋:Jari Rauham?ki、Tobias Rauter、Andrea H?ller、James Coplien、Uwe Zdun、Thomas Raser、Eden Burton、Claudius Link、Valentino Vrani和Sumit Kalra。特別感謝我的工作同事,尤其是Thomas Havlovec,他確保我在模式中的C 編程細節(jié)是正確的。Robert Hanmer、Michael Weiss、David Griffiths 和Thomas Krug 花了很多時間審查這本書,并為我提供了如何改進它的額外想法,非常感謝!也感謝OReilly 的整個團隊,在使這本書成為現(xiàn)實中給了我很多幫助。特別是,我想感謝我的開發(fā)編輯Corbin Collins 和我的產品編輯Jonathon Owen。
本書的內容基于以下在歐洲程序模式語言會議上被接受并在ACM 發(fā)表的論文。這些論文可以在http://www.preschern.com 網(wǎng)站上免費訪問。
? A Pattern Story About C Programming, EuroPLoP 21: 26th European Conference on Pattern Languages of Programs, July 2015, article no. 53,110, https://dl.acm.org/doi/10.1145/3489449.3489978.
? Patterns for Organizing Files in Modular C Programs, EuroPLoP 20:Proceedings of the European Conference on Pattern Languages of Programs,July 2020, article no. 1, 115, https://dl.acm.org/doi/10.1145/ 3424771.3424772.
? Patterns to Escape the #ifdef Hell, EuroPLop 19: Proceedings of the 24th European Conference on Pattern Languages of Programs, July 2019, article no. 2, 112, https://dl.acm.org/doi/10.1145/3361149.3361151.
? Patterns for Returning Error Information in C, EuroPLop 19: Proceedings of the 24th European Conference on Pattern Languages of Programs, July 2019, article no. 3, 114, https://dl.acm.org/doi/10.1145/3361149.3361152.
? Patterns for Returning Data from C Functions, EuroPLop 19: Proceedings of the 24th European Conference on Pattern Languages of Programs, July 2019, article no. 37, 113, https://dl.acm.org/doi/10.1145/3361149.3361188.
? C Patterns on Data Lifetime and Ownership, EuroPLop 19: Proc eedings of the 24th European Conference on Pattern Languages of Programs, July 2019,article no. 36, 113, https://dl.acm.org/doi/10.1145/3361149.3361187.
? Patterns for C Iterator Interfaces, EuroPLoP 17: Proceedings of the 22nd European Conference on Pattern Languages of Programs, July 2017, article no. 8, 114, https://dl.acm.org/doi/10.1145/3147704.3147714.
? API Patterns in C, EuroPlop 16: Proceedings of the 21st European Conference on Pattern Languages of Programs, July 2016, article no. 7, 111,https://dl.acm.org/doi/10.1145/3011784.3011791.
? Idioms for Error Handling in C, EuroPLoP 15: Proceedings of the 20th European Conference on Pattern Languages of Programs, July 2015, article no. 53, 110, https://dl.acm.org/doi/10.1145/2855321.2855377.