学习C/C++:伴随我成长的编程书!
學習C++是一個艱難的過程。如果從我第一次看C++的書算起,現在已經過了11年了。一開始的動機也是很不靠譜的。剛開始我很喜歡用VB6來開發游戲,但是我能找到的資料都是用C++來做例子的,文字部分又不豐富,于是我遇到了很多困難。因此我去三聯書店買了本C++的書,想著我如果學會了C++,就可以把這些例子翻譯成VB6的代碼,然后繼續用VB6來寫游戲。陰差陽錯,我買到的是一本語法手冊。不過那個時候我還小,不知道什么是MSDN,也不知道MSDN是可以打印出來賣的:
?
?
?
不過因為C++在當時并不是我學習的重點,于是我就沒事的時候翻一翻。我們都知道語言參考手冊(MSDN里面叫Language Reference)的順序都是按照類別而不是教學順序來排列的。于是當我花了很長時間看完了第一遍的時候,就覺得這本書寫的云里霧里。剛開始講什么是表達式的時候,例子就出現了大量的函數和類這種更加復雜的東西。于是我選擇重新看一遍,基本的概念就都知道了。當然這個時候完全不能算“學會C++”,編程這種事情就跟下象棋一樣,規則都很容易,但是你想要下得好,一定要通過長期的練習才能做到。
當然,在這段時間里面,我依然是一邊看C++一邊用VB6來學習編程。初二的時候學校發了QBasic的課本,當時看了一個星期就完全學會了,我覺得寫代碼很好玩,于是從此就養成了我沒事逛書店的習慣(就連長大了之后泡MM也有時候會去書店,哈哈哈哈哈)。值得一提的是,我第二次去書店的時候,遇到了下面的這本書《Visual Basic高級圖形程序設計教程》:
?
?
?
在這之前我買到的兩本VB6的書都是在教你怎么用簡單的語法,拖拖界面。然后就做出一個程序來。那個時候我心目中編程的概念就是寫寫記事本啊、寫字板啊、計算器等等這些東西,直到我發現了這本書。我還記得當時的心情。我在書架上隨手翻了翻,發現VB竟然也可以寫出那么漂亮的圖形程序。
這本書包含的知識非常豐富,從如何調用VB內置的繪圖命令、如何調用Windows API函數來快速訪問圖片,講到了如何做各種圖像的特效濾鏡、如何做幾何圖形的變換,一直到如何對各種3D物體做真實感渲染,甚至是操作4維圖形,都講得清清楚楚。這本書比其他大多數編程讀物好的地方在于,讀者可以僅靠里面的文字,基本不用看他的代碼,就可以學會作者想讓你學會的所有東西。因此當我發現我怎么著也找不到這本書的光盤(事實上書店就沒有給我)的時候,我并沒有感到我失去了什么。這本書的文字部分不僅寫得很詳細,而且作者還很負責任。作者知道像圖形這種對數學基礎有一定要求的東西,程序員不一定懂——尤其是我那個時候才上初中,就更不可能懂了——所以在書里面看到一些復雜的數學公式的時候,作者都會很耐心的告訴你這些公式的來源,它們的“物理意義”,有些時候甚至還會推導給你看。因此可以想象,這本書包含的內容也特別的豐富。這導致我在讀的時候不斷地找資料補充自己的數學知識,從而可以親自把那些程序寫(而不是抄)出來。這個過程一直持續到了我終于不用VB轉Delphi,到最后上大學改用C++的那個時候,我終于理解了整本書里面講的所有內容,給我后面的很多事情打下了堅實的基礎。
因為數學知識缺乏的關系,學習這些基礎知識又不可能那么快,所以我把一部分時間投入在了游戲開發里面,嘗試自己弄點什么出來。畢竟當時對編程有興趣,就是因為“說不定游戲也可以用代碼寫出來”的想法,于是我得到了下面的這本書:
?
?
?
這本書是我覺得21天驚天陰謀系列里面唯一一本良心的書。它并沒有只是簡單的羅列知識,而是教你利用VB6內置的功能搭建從簡單到復雜的游戲程序。我第一次看到關于鏈表的知識就是在這里。可惜在我還沒學會如何使用VB6的類模塊功能之前,我就已經投向了Delphi,因此并沒有機會實踐這個知識。不過在此之后,我用VB6寫的小游戲,已經嘗試把游戲本身的模塊(這是VB6的一個功能,就跟namespace差不多)分離,積累一些基礎代碼。
在這段時間里面,我學習語法都學得很慢。循環甚至是在我用人肉展開循環的方法一行一行復制黏貼出了一個井字棋的AI之后才學會的。后來很晚才學會了寫函數,全局變量則更晚了。于是在那個時候我寫了很多看起來很愚蠢的代碼。曾經我以為一個函數的全局變量在退出函數之后是會保留的,然后對著自己寫出來的不能運行的代碼感到十分的莫名其妙。還有一次做一個記事本,因為不知道“當前文件路徑”要存在什么地方,于是在界面上放了一個Label來放文件名。后來有了雄心壯志,想用VB搞定一個長得像Basic的超簡陋的腳本。這當然最后是失敗了,但是我依稀記得,我當時取得的成就就是把腳本語言的字符串分割成了一個一個的token之后,保存在了一個表格控件里面,以便之后(后來這個“之后”沒寫出來)讀的時候方便一點。之后還嘗試寫一個讀四則運算字符串計算結果的程序,都是先找最里層的括號,把那條不帶括號的簡單式子計算完之后,把結果也處理成字符串replace回去。直到整個字符串收斂成一個值為止。一直等到我后來買到了一本系統介紹VB6語法和用法的書之后,我的代碼才稍微變得不像猴子打出來的。
在剛開始學編程的時候,基本上都沒有什么固定的方向,都是在書店里面碰到什么酒寫什么。于是有一次我在書店里看到了《Visual Basic 網絡高級編程》
?
?
?
這本書是我在學習VB的過程中最后一本我覺得不錯的書了。雖然VB本身也提供了很多訪問網絡資源的控件,但是這本書并沒有讓你僅僅會用被人的輪子來寫代碼,而是一步一步的告訴你這些網絡協議的內容,然后讓你用Socket來跟這些服務器直接交互。我記得我最后成功的做出了一個郵件收發程序,跟聯想1+1系列自帶程序的功能已經可以媲美了。
二、
當我發現C++實在是太難,根本沒辦法真的把網上那些C++的程序改成VB之后,我上了高一,接觸了NOI。NOI讓我得到的一個收獲就是,讓我在上了大學之后很堅定的不把時間浪費在ACM上,從而有了很多時間可以搞圖形、編譯器和女同學。參加高中的NOI培訓讓我知道了什么是數據結構,還有什么是指針。老師在講Pascal的時候說,要靈活使用指針才可以寫出高性能的程序。這讓我大開眼界,不僅因為VB沒有指針,而且當時用VB寫圖形的程序感覺怎么樣也快不上去(當然這有大半原因是因為我代碼寫得爛,不能全怪VB)的同時,還讓我認識了Delphi。Delphi跟VB一樣可以拖控件,而且控件長得還很像。于是我就抱著試一試的心理,開始學習如何用Delphi來寫代碼。
因為有《Visual Basic 高級圖形程序設計教程》的知識作為背景,我很快就掌握了如何用Delphi來開發跟圖形相關的程序。那個時候我覺得該做的準備已經準備好了,于是用Delphi寫了一遍我在VB的時候總是寫不快的一個RPG游戲。這個游戲雖然不大,但是結構很完整。在開發這個游戲的過程中,我第一次體驗到了模塊化開發的好處,以及積累基礎代碼對開發的便利性。同時也讓我嘗到了一個難以維護的程序時多么的可怕。這個游戲前后開發了八個月,有一半的事件都是在寫代碼。對于當時的我來說,程序的結構已經過于復雜,代碼也多到差不多失控的地步了。后來我統計了一下,一共有一萬兩千行代碼。由于那個時候我的調試能力有限,而且也不知道如何把程序寫成易于調試的形式。結果我等到了我的核心部分都寫完了之后,才能按下F9做第一次的運行(!!!)。當然運行結果是一塌糊涂。我花了很大的努力才把搞到能跑。
由于程序本身過長,我在開發的過程中覺得已經很難控制了。再加上我發現我的同一個模塊里的函數基本上都是下面的形式:
PrefixFunction(var data:DataStructure, other parameters …)
總覺得跟調用Delphi的類庫的時候很像。所以我就想,既然代碼都變成了這樣,那是不是學習面向對象開發會好一點?在這個過程中我有幸遇到了這本《Delphi6 徹底研究》:
?
?
?
雖然說這本書并沒有包含那些深刻的面向對象的知識,但是他詳細的介紹了Delphi的語法、基礎的類庫的用法還有Delphi那套強大的控件庫和數據開發的能力。這本書第一次讓我知道,Delphi是可以內嵌匯編代碼的。這給我對計算機的深入理解打開了一扇門。
學習匯編是一個漫長的過程。這倒不是因為匯編的概念很復雜,而是因為里面的細節實在是太多了。這些知識靠網絡上零星的文章實在是無法掌握,于是在常年逛書店的習慣之下,我又遇到了《Windows 匯編語言程序設計教程》
?
?
?
這本書內容其實并不是很多,但是他給了我一個很好的入門的方法,也講了一些簡單的匯編的技巧,譬如說怎么寫循環啊,怎么用REPZ這樣的前綴等等,讓我可以用匯編寫出有意義的程序。匯編和Delphi的結合也促使我開始去思考他們之間的關系,譬如說一段Delphi的代碼就經是如何映射到匯編上面的。下面發生的一個小故事讓我印象深刻。
那還是一個,我還很喜歡各種不知所謂的奇技淫巧的日子。有一天我在論壇里看到有人說,交換兩個integer變量可以用一種奇葩的寫法:
a:=a xor b;
b:=b xor a;
a:=a xor b;
于是我就理所當然得想,如果我把它改成匯編,那是不是可以更快,并且超過那種需要中間變量的寫法?后來我試了一次,發現慢了許多。這個事件打破了我對會變的迷信,當然什么C語言是最快的語言之類的,我從此也就以辯證的眼光去看帶了。在接下來的高中生涯里,我只用了匯編一次,那還是在一個對圖像做alpha blending的程序里面。我要同時計算RGB,但是寄存器每一個都那么大,我覺得很浪費,于是嘗試用R<<16+G放到一個寄存器里面,跟另一個R<<16+G相加。中間隔了一個字節用來做進位的緩沖,從而達到了同時計算兩個byte加法的效果。后來測試了一下,的確比直接用Delphi的代碼來寫要快一些。
純粹的教程類書籍看多了之后,除了類庫用得熟、代碼寫得多以外,好處并不大。所以當我有一天在書店里發現《凌波微步》的時候,剛翻開好幾頁,我就被它的內容吸引住了,斷然入手。
?
?
?
這本書讓我第一次覺得,一個程序寫得好和寫得爛竟然有如此之大的差別。作者下筆幽默,行文詼諧,把十幾個例子用故事一般的形式講出來。這本書不告訴你什么是好的,而告訴你什么是不好的。每一個案例的開頭都給出了寫得不好的代碼的例子,然后會跟你解釋的很清楚,說這么做有什么不好,改要怎么改的同時,為什么好的方法是長那個樣子的。這本書也開始讓我相信方法論的意義。在這個時候之前,我在編程這個東西上的理論基礎基本上就只有鏈表和排序的知識,其它的東西基本都不懂,但是想做出自己想要做的事情卻又不覺得有什么太大的麻煩。甚至我到高三的時候寫了一個帶指令集和虛擬機的Pascal腳本語言(不含指針)的時候,我連《編譯原理》這本書都沒有聽過。因此以前覺得,反正要寫程序,只要往死里寫,總是可以寫出來的。但是實際上,有理論基礎和沒有理論基礎的程序員之間的區別,不在于一個程序能不能寫出來,而在于寫出來之后性能是不是好,代碼是不是容易看懂的同時還很好改,而且還容易測試。這本書對于我的意義就是給我帶來了這么一個觀點,從而讓我開始想去涉獵類似的內容。
當然,那段時間只是這么想,但是卻不知道要看什么。所以在一次偶然之下,我發現了《OpenGL 超級寶典》。當然第一次看的時候還是第二版,后來我又買了第三版。
?
?
?
鑒于以前因為《Visual Basic 高級圖形程序設計教程》的緣故,我在看這本書之前已經用Delphi寫過一個簡單的支持簡單光照和貼圖的軟件渲染程序,于是看起來特別的快。其實OpenGL相比起DirectX,入門級的那部分API(指glBegin(GL_TRIANGLE_STRIP)這些)是做得比DirectX漂亮的,可惜性能太低,沒人會真的在大型游戲里使用。剩下的那部分比DirectX就要爛多了。所以當我開始接觸高級的API的時候,OpenGL的低速部分讓我戀戀不舍。OpenGL的程序我一路寫到了差不多要高考的時候。在那之前學習了一些簡單的技巧。上了大學之后,學習了一些骨骼動畫啊、LOD模型啊、場景管理這些在OpenGL和DirectX上都通用的知識,但是卻并沒有在最后把一個游戲給做出來。
我最后一次用OpenGL,是為了做一個自繪的C++GUI庫。這個庫的結構比起現在的GacUI當然是沒法。當時用OpenGL來做GUI的時候,讓我感覺到要操作和渲染字符串在OpenGL上是困難重重,已經難到了幾乎沒辦法處理一些高級文字效果(譬如RichText的渲染)的地步了。最后只能每次都用GDI畫完之后把圖片作為一個貼圖保存起來。OpenGL貼圖數量有限,為了做這個事情還得搞一個貼圖管理器,把不同的文字都貼到同一張圖上。做得筋疲力盡之余,效果還不好。當我后來開發GacUI的時候,我用GDI和DirectX作為兩個渲染器后端,都成功的把RichText渲染實現出來了,我就覺得我以后應該再也不會使用OpenGL了。GDI和DirectX才是那種完整的繪圖API,OpenGL只能用來畫圖,寫不了字。
有些人可能會覺得,為什么我會一直在同時做圖形圖像、編譯器和GUI的事情。大家還記得上文我曾經說過我曾經用了好久做了一個伊蘇那種模式的RPG出來。其實我一直都很想走游戲開發的路線,可惜由于各種現實原因,最后我沒有把這件事情當成工作。做出那個RPG的時候我也很開心,絲毫不亞于我畢業后用C#寫出了一個帶智能提示的代碼編輯器的那一次。當然在上大學之后我已經覺得沒有一個美工是做不出什么好游戲的,但是想花時間跟你一起干的美工同學又很難找,因此干脆就來研究游戲里面的各種技術,于是就變成了今天這個樣子。當然,現在開發游戲的心思還在,我想等過些時日能夠空閑了下來,我就來忽悠個美工妹紙慢慢搞這個事情。
雖然說《Visual Basic高級圖形程序設計教程》是一本好書,但這只是一本好的入門書,想要深入了解這方面的內容還是免不了花時間看其他材料的。后來我跟何詠一起做圖形的時候,知識大部分來源于論文。不過圖像方面,還是下面這本岡薩雷斯寫的《數字圖像處理》給了我相當多的知識。
?
?
?
這本書的特點是,里面沒有代碼,我很喜歡,不會覺得浪費錢。不過可惜的是在看完這本書之后,我已經沒有真的去寫什么圖像處理的東西了。后面做軟件渲染的時候,我也沒有把它當成我的主業來做,權當是消磨時間。每當我找不到程序可以寫覺得很傷心的時候,就來看看論文,改改我那個軟件渲染器,增加點功能之后,我就會發現一個新的課題,然后把時間都花在那上面。
三、
整個高三的成績都不錯,所以把時間花在編程上的時候沒人理我,直到我二模一落千丈,因此在高考前一個月只好“封筆”,好好學習。最后因為失誤看錯了題目,在高考的時候丟了十幾分的原始分,估計換算成標準分應該有幾十分之多吧,于是去了華南理工大學。所幸這本來就是我的第一志愿,所以當時我也不覺得有什么不開心的。去了華南理工大學之后,一個令我感到十分振奮的事情就是,學校里面有圖書館,圖書館的書還都不錯。雖然大部分都很爛,但是因為基數大,所以總能夠很輕松的找到一些值得看的東西。
我還記得我們那一年比較特殊,一進去就要軍訓。軍訓的時候電腦還沒來得及帶去學校,學校也不給開網絡,所以那一個月的晚上都很無聊,跟同學也還不熟悉,不知道要干什么。所以那段時間每到軍訓吃晚飯,我就會跑到學校的圖書館里面泡到閉館為止。于是有一天讓我發現了李維寫的這本《Inside VCL》。
?
?
?
雖然到了這個時候我用Delphi已經用得很熟悉了,同時也能寫一些比較復雜的程序了,但是對于Delphi本身的運作過程我是一點都不知道。所以當我發現這本書的時候,如魚得水。這本書不僅內容深刻,更重要的是寫的一點都不晦澀難懂,所以我看的速度非常快。基本上每個晚上都可以看100頁,連續七八天下來這本書就被我翻完了。這帶來了一個副作用就是,圖書館的姐姐也認識我了——當然這并沒有什么用。
過后我又在書店得到了一本《Delphi 源代碼分析》。
?
?
?
這本書跟《Inside VCL》的區別是,《Inside VCL》講的是VCL的設計是如何精妙,《Delphi 源代碼分析》講的則是Delphi本身的基礎設施的內部實現的細節。以前我從來不了解也沒主動想過,Delphi的AnsiString和UnicodeString是指向一個帶長度記錄的字符串指針,學習了指針我也沒把這兩者聯系起來(當然這跟我當時還沒開始試圖寫C++程序有關)。于是看了這本書,我就有一種醍醐灌頂的感覺。雖然這一切看起來都是那么的自然,讓我覺得“就是應該這么實現的才對”,但是在接觸之前,就是沒有去想過這個事情。
令人遺憾的是,在我得到這本書的同時,Borland也把Delphi獨立出來做了一個叫做Codegear的公司,后來轉手賣掉了。我在用Delphi的時候還想著,以后干脆去Borland算了,東西做得那么好,在那里工作肯定很開心。我在高中的時候還曾經把Borland那個漂亮的總部的圖片給我媽看過,不過她一直以為是微軟的。于是我在傷心了兩個晚上之后,看了一眼為了做參考我帶到學校來的《Visual C++ 5.0語言參考手冊》,找了一個盜版的Visual C++ 2005,開始決定把時間投入在C++上面了。于是Delphi之旅到此結束,從此之后,就是C++的時光了。
四、
學習圖形學的內容讓我學會了如何寫一個高性能的計算密集型程序,也讓我不會跟很多程序員一樣排斥數學的內容。學習Delphi讓我開闊了眼界的同時,還有機會讓我了解Delphi內部工作原理和細節。這一切都為我之后做那些靠譜的編譯器打下了基礎。
因為在高三的時候我在不懂得《編譯原理》和大部分數據結構的知識的情況下,用Delphi寫出了一個Pascal腳本引擎,所以當我聽說我大學的班主任是教編譯原理的時候,我就很開心,去跟她交流這方面的內容,把我當時的設想也拿給她看。當然我的設想,沒有理論基礎的知識,都是很糟糕的,于是班主任就給了我一本《編譯原理》。當然,這并不是《龍書》,而是一本質量普通的書。不過當我了解了這方面的內容之后,《龍書》的大名也就進入我的耳朵里了:
?
?
?
由于之前用很愚蠢的方法寫了個Pascal腳本的緣故,看《龍書》之后很容易就理解了里面各種精妙的算法在工程上的好處。我之前的作法是先用掃描的方法切下一個一個的token,然后做一個遞歸來遞歸去復雜到自己都沒法看的一遍掃描生成簡單指令的方法來做。程序寫出來之后我當場就已經看不懂了。自從看了《龍書》之后,我才知道這些過程可以用token和語法樹來對算法之間進行解耦。不過《龍書》的性質也是跟《Visual Basic 高級圖形程序設計教程》一樣,是入門類的書籍。用來理解一下編譯器的運作過程是沒問題的,但是一旦需要用到高級的知識。
這個時候我已經初步理解了編譯器前端的一些知識,但是后端——譬如代碼生成和垃圾收集——卻還是一知半解。不過這并不妨礙我用好的前端知識和爛的后端知識來做出一個東西來。當時我簡單看了一下Java語言的語法,把我不喜歡的那些東西砍掉,然后給他加上了泛型。Java那個時候的泛型實現好像也是剛剛出現的,但是我不知道,我也從來沒想過泛型要怎么實現。所以當時我想來想去做了一個決定,泛型只讓編譯器去檢查就好了,編譯的時候那些T都當成object來處理,然后就把東西做出來了。我本來以為我這種偷工減料拆東墻補西墻忽悠傻逼用戶的方法是業界所不容的,不過后來發現Java竟然也是那么做的,讓我覺得我一定要黑他一輩子。后來我用我做的這個破語言寫了一個俄羅斯方塊的游戲,拿給了我的班主任看,向她證明她拿給我的書我沒有白看。
不過由于受到了Delphi的影響,我并沒有在我的C++代碼里面使用泛型。當時由于不了解STL,也懶得去看,于是自己就嘗試折騰這么幾個容器類自己用。現在代碼還留著,可以給大家貼一段:
?
總結
以上是生活随笔為你收集整理的学习C/C++:伴随我成长的编程书!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 超级实用!如何为机器学习算法准备数据?
- 下一篇: 火爆 GitHub 的《机器学习 100