前言
你拿起這本書是為了提升你的編程技能。很好,因為你一定會從這本書提供的實踐知識中受益。如果你有豐富的C 語言編程經(jīng)驗,你將學(xué)習(xí)到良好設(shè)計決策的細節(jié),以及它們的優(yōu)缺點。如果你對C 語言編程還比較陌生,你會找到關(guān)于設(shè)計決策的指導(dǎo),并且你會看到這些決策如何一點一點地應(yīng)用到運行的代碼示例中,以構(gòu)建更大規(guī)模的程序。
這本書回答了如何構(gòu)建C 程序、如何應(yīng)對錯誤處理、如何設(shè)計靈活接口等問題。
當(dāng)你對C 語言編程了解得更多時,經(jīng)常會出現(xiàn)以下問題:
? 我應(yīng)該返回任何錯誤信息嗎?
? 我應(yīng)該使用全局變量errno 來做這件事嗎?
? 我應(yīng)該有幾個參數(shù)很多的函數(shù)還是相反?
? 我如何構(gòu)建一個靈活的接口?
? 我如何構(gòu)建基本的東西,比如迭代器?
對于面向?qū)ο笳Z言,這些問題大多數(shù)在《設(shè)計模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書中得到了很大程度的解答,該書由Erich Gamma、Richard Helm、Ralph Johnson 和John Vlissides 編著(Prentice Hall,1997)。設(shè)計模式為程序員提供了有關(guān)對象如何交互以及哪個對象擁有哪種其他對象的最佳實踐。
此外,設(shè)計模式展示了這些對象如何組合在一起。
然而,對于像C 這樣的過程編程語言,大多數(shù)這些設(shè)計模式無法按照四人組描述的方式實現(xiàn)。C 中沒有原生的面向?qū)ο髾C制。雖然可以在C 編程語言中模擬繼承或多態(tài),但這可能不是首選,對于那些使用C 編程且不熟悉面向?qū)ο笳Z言(如C )以及繼承和多態(tài)等概念的程序員,可能更愿意堅持他們熟悉的原生C 編程風(fēng)格。然而,采用原生C 編程風(fēng)格,并不是所有面向?qū)ο笤O(shè)計模式的指導(dǎo)都適用,或者至少對于非面向?qū)ο缶幊陶Z言,無法提供設(shè)計模式中所呈現(xiàn)的特定實現(xiàn)思路。
我們所面臨的情況是:我們想在C 中進行編程,但不能直接應(yīng)用設(shè)計模式中的大部分知識。本書展示了如何填補這個差距,將實際的設(shè)計知識應(yīng)用于C編程語言中。
我為什么寫這本書?
讓我來告訴你為什么本書中所匯集的知識對我來說非常重要,以及為什么這樣的知識很難找到。
在學(xué)校里,我學(xué)習(xí)了C 編程作為我的第一門編程語言。就像每個新的C 程序員一樣,我想知道為什么數(shù)組索引從0 開始,我一開始甚至隨機嘗試如何放置運算符* 和&,來弄懂C 指針里的魔法到底如何起作用的。
在大學(xué)里,我學(xué)習(xí)了C 語法的實際工作原理以及它如何在硬件上轉(zhuǎn)化為位和字節(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ā)出許多問題:
? 為什么在結(jié)構(gòu)體中需要函數(shù)指針?
? 為什么函數(shù)需要那個DRIVER_HANDLE ?
? IOCTL 是什么,為什么我不能使用單獨的函數(shù)?
? 為什么必須顯式的創(chuàng)建和銷毀函數(shù)?
當(dāng)我開始編寫產(chǎn)品級的應(yīng)用程序時,這些問題就浮現(xiàn)出來了。我經(jīng)常遇到一些情況,讓我意識到我沒有足夠的C 編程知識,例如,我無法決定如何實現(xiàn)迭代器,或者決定如何在我的函數(shù)中進行錯誤處理。我意識到,盡管我了解C 語法,但我不知道如何應(yīng)用它。我試圖做一些事情,但只是以笨拙的方式或根本無法實現(xiàn)。我需要的是關(guān)于如何使用C 編程語言實現(xiàn)特定任務(wù)的最佳實踐。例如,我需要知道類似以下的事情:
? 我如何以簡單的方式獲取和釋放資源?
? 在錯誤處理中使用goto 是個好主意嗎?
? 我是應(yīng)該將接口設(shè)計得靈活,還是在需要時直接更改它?
? 我是應(yīng)該使用assert 語句,還是應(yīng)該返回錯誤代碼?
? 在C 中如何實現(xiàn)迭代器?
對我來說,非常有趣的一點是,雖然我的有經(jīng)驗的同事們對這些問題有很多不同的答案,但沒有人告訴我哪里有了解這些設(shè)計決策及其利弊的文檔。
所以接下來我轉(zhuǎn)向了互聯(lián)網(wǎng),然而我再次感到驚訝:盡管C 編程語言已經(jīng)存在了幾十年,但很難找到這些問題的答案。我發(fā)現(xiàn),雖然有很多關(guān)于C 編程語言基礎(chǔ)和語法的文獻,但很少涉及高級C 編程主題,或者告訴人們?nèi)绾尉帉懩軌驊?yīng)對產(chǎn)品級應(yīng)用的優(yōu)美C 代碼。
而這正是這本書的用武之地。本書教會你如何提升從編寫基本的C 程序,轉(zhuǎn)而到編寫考慮錯誤處理的大規(guī)模C 程序,并對未來的需求和設(shè)計變化保持靈活的編程技能。本書使用設(shè)計模式的概念,逐步為您提供設(shè)計決策及其利弊。這些設(shè)計模式被應(yīng)用于運行的代碼示例,教會你為什么先前的示例代碼會演變成現(xiàn)在的樣子。
這些呈現(xiàn)的模式可以應(yīng)用于任何C 編程領(lǐng)域。由于我來自多線程實時環(huán)境的嵌入式編程領(lǐng)域,因此一些模式可能會偏向于該領(lǐng)域。不管怎樣,你將會看到這些模式的通用思想可以應(yīng)用于其他C編程領(lǐng)域,甚至超越了C編程的范圍。
模式基礎(chǔ)
本書中的設(shè)計指導(dǎo)以模式的形式呈現(xiàn)。將知識和最佳實踐以模式的形式呈現(xiàn)的想法源自建筑師克里斯托弗·亞歷山大的《建筑的永恒之道》(牛津大學(xué)出版社,1979)。他使用經(jīng)過驗證的小型解決方案來解決他所在領(lǐng)域的一個巨大問題:如何設(shè)計和建造城市。這種應(yīng)用模式的方法被軟件開發(fā)領(lǐng)域采納,其中舉辦了一些關(guān)于模式的會議,如模式語言程序會議(PLoP)等,來用于擴展關(guān)于模式的知識體系。特別是由四人組(Gang of Four)《設(shè)計模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書(Prentice Hall,1997),對軟件開發(fā)人員產(chǎn)生了重要影響,使設(shè)計模式的概念為軟件開發(fā)人員廣為人知。
但是,什么是模式?市面上有很多定義,如果您對此主題非常感興趣,那么弗蘭克·布施曼(Frank Buschmann)等編寫的《面向模式的軟件架構(gòu):模式和模式語言》(Wiley,2007)一書可以為你提供準(zhǔn)確的描述和細節(jié)。在本書中,模式為實際問題提供了經(jīng)過驗證的解決方案。本書中呈現(xiàn)的模式具有的結(jié)構(gòu)如表1 所示。
表1:本書中呈現(xiàn)的模式
模式部分 說明
名字 這是模式的名稱,應(yīng)該容易記住。目標(biāo)是讓程序員在日常語言中使用這個名稱(就像四人組模式一樣,程序員會說:抽象工廠創(chuàng)建對象)。本書中的模式名稱以大寫字母書寫
上下文 上下文部分為模式設(shè)置了背景。它告訴你在哪些情況下可以應(yīng)用這種模式問題 問題部分向您提供了你想要解決的問題的信息。它以粗體字體開始,列出主要的問題陳述,然后詳細說明為什么這個問題很難解決(在其他模式格式中,這些詳細信息放在一個稱為forces的單獨部分中)
解決方案 這一部分提供了如何解決問題的指導(dǎo)。它以粗體字體陳述解決方案的主要思想,然后繼續(xù)詳細說明解決方案。它還通過提供代碼示例來提供非常具體的指導(dǎo)結(jié)果 這一部分列出了應(yīng)用所描述解決方案的利弊。在應(yīng)用模式時,你應(yīng)該始終確認產(chǎn)生的結(jié)果是否符合預(yù)期
已知用例 已知用例提供了所提出的解決方案在實際應(yīng)用中是有效的證據(jù)。它們還向你展示具體的示例,幫助你理解如何應(yīng)用該模式
以模式的形式呈現(xiàn)設(shè)計指導(dǎo)的一個重要優(yōu)勢是這些模式可以應(yīng)用在各個場景。如果你面臨的是一個巨大的設(shè)計問題,將很難找到一個確切的指導(dǎo)文檔或解決方案,可以恰好解決這個問題。相反,你可以將巨大且特定的問題視為許多較小和更通用的問題的總和,并通過逐個應(yīng)用模式來逐步解決這些問題。你只需檢查模式所對應(yīng)的問題描述,選擇并應(yīng)用適合你的問題的模式,并得到一些結(jié)果。這些模式應(yīng)用的結(jié)果可能會導(dǎo)致另一個問題,你可以通過接著應(yīng)用另一個模式來解決。通過這種方式,逐步地設(shè)計你的代碼,而不是試圖在編寫第一行代碼之前提前產(chǎn)出一個完整的設(shè)計。
如何閱讀這本書?
你應(yīng)該對C 語言的基礎(chǔ)知識有了一些了解。你應(yīng)該知道C 語言的語法以及它是如何工作的。例如,這本書不會教你什么是指針或如何使用它。這本書旨在對于一些高級抽象地話題提供提示和指南。
本書中的章節(jié)是獨立的。你可以按任意順序閱讀它們,并且你可以簡單地挑選出你感興趣的話題。在接下來的一段,你會找到所有模式的概覽,從那里你可以跳轉(zhuǎn)到你感興趣的模式。所以,如果你確切地知道你在尋找什么,直接開始就好了。
如果你不是在尋找某一個特定的模式,而是想要對可能的C 程序的設(shè)計選項有一個概覽,請通讀本書。在本書中,每章都關(guān)注一個特定的話題,從如錯誤處理和內(nèi)存管理等基本話題開始,然后會轉(zhuǎn)移到更高級和特定的話題,如接口設(shè)計或平臺獨立代碼。在每章中,都會介紹與該話題相關(guān)的模式,以及逐步展示如何應(yīng)用這些模式運行代碼示例。
本書的部分展示了兩個較大的運行示例,在這兩個例子里應(yīng)用了許多的模式。
在這里,你可以學(xué)習(xí)如何通過應(yīng)用模式逐步構(gòu)建一些更加大型的軟件。
OReilly 在線學(xué)習(xí)平臺(OReilly Online Learning)
近40 年來,OReilly Media 致力于提供技術(shù)和商業(yè)培訓(xùn)、知識和卓越見解,來幫助眾多公司取得成功。
公司獨有的專家和改革創(chuàng)新者網(wǎng)絡(luò)通過OReilly 書籍、文章以及在線學(xué)習(xí)平臺,分享他們的專業(yè)知識和實踐經(jīng)驗。OReilly 在線學(xué)習(xí)平臺按照您的需要提供實時培訓(xùn)課程、深入學(xué)習(xí)渠道、交互式編程環(huán)境以及來自O(shè)Reilly 和其他200 多家出版商的大量書籍與視頻資料。更多信息,請訪問網(wǎng)站:https://www.oreilly.com/。
聯(lián)系我們
任何有關(guān)本書的意見或疑問,請按照以下地址聯(lián)系出版社。
美國:
OReilly Media, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
中國:
北京市西城區(qū)西直門南大街2 號成銘大廈C 座807 室(100035)
奧萊利技術(shù)咨詢(北京)有限公司
我們?yōu)楸緯渲昧藢倬W(wǎng)頁,在上面列出了勘誤表、示例以及其他附加信息。
你可以通過https://oreil.ly/fluent-c 來訪問。
如果你對本書有一些評論或技術(shù)上的建議,請發(fā)送電子郵件到errata@oreilly.com.cn。
要了解OReilly 的圖書和培訓(xùn)課程的新聞和信息,請訪問我們的網(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。她們兩個都讓我的生活更加快樂,確保我不總是坐在電腦前工作,而是享受生活。
沒有許多模式愛好者的幫助,這本書不可能面世。我想感謝所有在歐洲程序模式語言會議上的作家工作坊的參與者為我提供關(guān)于模式的反饋。特別是,我想感謝以下人員,在會議的所謂的牧羊人過程中為我提供了非常有用的反饋: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 和我的產(chǎn)品編輯Jonathon Owen。
本書的內(nèi)容基于以下在歐洲程序模式語言會議上被接受并在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.