怎樣才算一個計算機知識體系完整的畢業生

為什么突然想寫這個話題呢?最近有不少新關注的讀者,在后臺問:大學學 Java 和 C++ 哪個好找工作,學前端好還是后端好,該學 Vue 還是 React。。。仿佛看到了自己當年的模樣,所以覺得有必要單獨寫一篇文章,單純以一個計算機應屆畢業生的身份聊聊,我認為大學四年,計算機科班學生應該學些什么,哪些才是重點。同樣大學四年,為什么有些同學畢業就能成為大廠 Offer 收割機,各種 SP、SSP 拿到手軟,有的同學明明在學校寫了好多網站,項目經歷滿滿,經歷春秋招,卻找到一份工作都很難。不能說后者沒認真學習,或許是用力的方向不對。話不多說,直接進正題吧。

人類的知識邊界一直在不斷的擴張,俗話說學無止境,這放在計算機領域也同樣適用,計算機本身是一個人造科學,不屬于自然科學。每年,甚至每個月都不斷有新的編程框架推出,學到頭禿你也學不完,也沒有必要去挨個學。并且你會發現,很多一二線大廠內部用的東西基本都是自己搞一套的,比如服務發現、RPC、KV、DB、消息隊列、日志、監控等等。所以一般這些大廠招聘的時候基本不會因沒學過某種框架而掛你,反正很多東西都是要進來重新學的。他們會更加關注你的基礎知識、解決問題的經驗以及聰明度這種更加通用的能力上。反而是一些小公司,可能會要求你必須會 Spring、Vue、Redis... 這些框架或者組件。

在這里,我粗淺的把計算機編程領域的知識分為三個部分:

基礎知識

特定領域知識

框架和開發技能

基礎知識是指不管從事任何方向的軟件工程師都應該掌握的,比如數據結構、算法、操作系統。特定領域知識就是你從事某個細分方向時需要掌握的知識,比如做游戲引擎的需要掌握圖形學;做前端的需要掌握瀏覽器渲染原理、前端三大件;算法工程師需要更多的數學知識。畢竟計算機各種門類挺多的,需要選個細分方向專研下去,什么都學只會什么都不精(大佬除外啦。

現在大環境比較浮躁,很少有人愿意花心思在基礎上,喜歡直接學 Python 搞機器學習、寫秒殺、做商城。找工作的時候都是想看面經、總結速成。但是作為優秀的計算機系學生的你怎么能流于各種編程框架(造框架除外),糾結學 SpringBoot 還是 SSH 呢?把時間花在算法、基礎學科上他不香嗎?功利一點講,回報反而會更大。況且在計算機領域,很多基礎的理論并不十分高深,我們努努力就可以掌握其中的核心知識。

一、基礎知識

1.1 數學

首先說明,這里把數學列出來不是為了顯得高端,而是自己吃過數學的虧。如果你是自學轉行當程序員,我當然不會推薦數學,因為轉行的大概率是去學 Java、前端這類,對數學基本沒啥要求。但是這篇文章主要面向的是還在大學的科班學生,這部分同學以后也許會去做算法(CV、NLP之類)、游戲引擎、信息安全編碼等這些方向對數學要求就會偏高,在計算機領域,線代、概率論、統計學這些數學分支相對比較重要,計算機本質上還是離散的。比如在機器學習或數據挖掘中常常用線性代數來降低數據維度,很多問題最終都能化為求解線性方程組。所以為了避免以后想走這些方向卻被數學卡住,在大一、大二上數學課的時候就好好的學一下。書到用時方恨少,不要現在以為沒用處就不好好學,等你需要的時候,就知道后悔了。(默默流下了不學無術的眼淚┭┮﹏┭┮什么?你說以后肯定做開發方向?那的確可以把數學優先級放后面一點,用得確實不多,不過上數學課的時候總該認真聽下吧,拿個高績點也是百利無一害嘛。說不準哪天你又想加入算法內卷大軍呢?

1.2 C語言

你也許會很疑惑,這里明明說基礎知識,為什么要把一門編程語言單獨列出來呢?因為在我看來,沒有比 C 語言更適合用來理解計算機系統了。我們后面將會提到的操作系統、體系結構 這些東西非常適合用 C 語言去理解或者去實踐。并且 C 語言本身的語言特性非常少,但是想學好又是不容易,很多人都覺得 C 語言難,難在哪里呢?回想了一下我大一時的感受:

簡陋的標準庫,幾乎沒有可用的數據結構和算法,什么都得自己來

指針很難理解和使用

需要了解匯編、鏈接、裝載、內存等才能把 C 語言用好

不巧的是,這些東西正是計算機系統知識的一部分,所以用 C 語言作為學習計算機系統知識是最有效率的方式。真的很難想象用 Java 或是 Python 去給別人講解內存,因為這些語言抽象程度都比 C 語言高,意味著離計算機系統也就越遠。在 TIOBE 編程語言排行榜上,C語言幾乎永遠占據前三位,其地位自然毋庸置疑。

而且幾乎你開發中用到的很多東西都是用C語言編寫的,Linux、Nginx、Redis、MySQL、Git......或許你會想要探究下原理,閱讀點這些開源軟件的源碼,那么 C 語言也是你必備的瑞士軍刀。深入學習 C 語言,能夠了解計算機底層的執行原理,是理解程序運行機制的絕佳語言,無出其右。

所以對于計算機科班來說,不管你是做前端還是后端,算法還是開發,C 語言都建議你好好學習。這是無關方向的一門語言,就是基礎!

1.3 操作系統

我們編程的 IDE、寫出來的程序全部都需要運行在操作系統上,說操作系統是計算機軟件的基石也不為過。程序運行起來就需要創建進程,這涉及到操作系統的進程管理;寫程序需要定義變量、存儲數據吧,這又涉及到內存,對應內存管理;有時候我們還需要讀寫文件,這又離不開和文件系統打交道;你需要學習使用鎖、條件變量、臨界區來保證程序并發執行時不會錯亂。而讀寫文件、分配內存這些又離不開系統調用(System call)。并且當你真正做起工程就會發現,很多問題是和操作系統緊密相關的,不理解操作系統,你連問題的原因都分析不出來。比如前段時間我們出現的在基于協程(libco)的框架下,使用多線程的鎖去做同步互斥偶發死鎖,后來分析才發現原因:由于協程是應用層實現的,一個線程內多個協程對于操作系統是感知不到的:

當一個協稱 A 上鎖后發起網絡 IO 請求,這個時候會被切換到另外一個協程B,而協程 B 又去請求這個鎖。那么這個時候操作系統會認為這個鎖已經被上了,因此會將協程 B 對應的線程掛起到等待隊列,這樣的話就導致協程 A 永遠無法運行,也就無法釋放鎖,導致死鎖。解決的方法也很簡單,就是將鎖設置為可重入鎖,可重入意味著同一個線程多次去請求同一個鎖不會導致掛起。這樣當協程 B 再去請求鎖的時候,操作系統就會認為協程 B 所在的線程已經持有這個鎖了,直接返回,繼續執行。總之,我們寫程序每時每刻都在和操作系統交互,沒有理由不學好。

1.4 編譯原理

編譯原理可能是我們平時接觸得最少的了,大家也許會覺得自己又不用去造新的編程語言,學編譯原理干啥。學好編譯原理有啥用?你會站在更高的角度去審視這些編程語言,看到的不再是表面的語法,更會想到語法背后的實現。這種感覺很透徹,就像搞懂了操作系統、體系結構你會明白一個程序從雙擊鼠標開始,到底是如何被運行起來的,這種掌握一切細節,透徹的感覺,真的很奇妙,不信你去試試。說人話!那學了編譯原理你能干啥?當你學完有限狀態機以后,你會發現以前覺得很牛逼正則表達式似乎自己也能用 DFA、NFA 實現一下了。狀態機的思想在編程中很多地方都用得上。比如解析 HTTP 協議,如果沒學過狀態機思想,你可能會一行行的 if/else 去做解析,這里最麻煩的地方在于,if/else 需要提前將 HTTP 頭部字段都接收到再來判斷,而我們知道 HTTP 基于 TCP,而 TCP 是流式傳輸,所以你很有可能是幾個字符一組組接收到的,這個時候用 if/else 寫出來就很難看了。而用狀態機編寫起來代碼就會非常優雅。狀態的轉移是由規則驅動的,接收到一個字符就判斷一個,非常的方便。繼續學完語法分析,你會掌握遞歸下降分析這樣非常重要的思想,你可以使用遞歸下降快速的實現四則運算計算器。如果不用遞歸下降你可能需要先中綴表達式轉后綴,然后求值,這是我們大一數據結構課寫的,當時用棧寫的,有點麻煩。后來學完編譯原理,又用遞歸下降重寫了一遍,區區幾十行代碼遍搞定。還有一類場景在實際開發中的用的很多,比如淘寶、京東這樣的電商,它們的營銷規則有很多,比如滿減、直減、跨店等等,這樣的規則是不可能寫死在代碼里的。那是怎么做的呢?一般會實現一個配置系統,并設計一個DSL(領域特定語言)來表達這些規則,將規則直接配置到系統中,這樣可以非常方便的修改,那么如何在代碼里去解析 DSL 定義的規則呢?這就需要為 DSL 寫一個語法解析器,這里就會用到語法分析的方法。DSL(Domain Specific Language),是一種用于某個特定領域的程序設計語言。這種特定于某個領域是相對于 C、C++、Python 這種通用語言而言的,通用語言可以在各個領域使用,我們熟悉的大多數程序設計語言都是通用語言,它們都是圖靈完備的。像我們平常經常使用的 JSON、SQL、HTML 這些都算是一種 DSL,你甚至可以嘗試用遞歸下降去寫一個 JSON、XML 解析器,這比寫電商網站更有價值的。繼續往下學你會了解到抽象語法樹 AST 如何生成、如何轉化為中間代碼、如何對中間代碼優化、最終又是怎么生成機器指令的。你會看到貪心算法在寄存器分配中的應用,也會看到圖論中的可達性分析又是如何實現死代碼消除。IDE上面那個綠色的編譯按鈕對你不再是黑魔法。為啥點一下就能生成可執行的程序?你寫的英文字母又是如何變成一個個二進制指令的?學完編譯原理,這些通通不是問題,媽媽再也不用擔心你的學習~當然完成一個像 GCC、Clang 這樣的編譯器難度太高太高,我們學習編譯原理的目的也不是去造這樣的輪子,而是為了更好的理解和運用編程語言。

1.5 體系結構&組成原理

上面說的都是軟件層面,體系結構則是關于計算機是如何工作的,你會了解到典型的存儲程序計算機是怎樣運轉的。記得南大有個老師說過 “我們不是學習使用計算機的,而是學習如何造計算機”,雖然造計算機有點夸張,但是至少我們得了解下計算機的實現原理,了解下代碼是怎么被 CPU 執行的吧?不然其實你會很困惑,明明一堆英文字母,怎么在 CPU 這種電路上跑起來的,我大一學 C 語言就百思不得其解,直到后來學了組成原理和數字邏輯。我們說計算機中一切都是 0、1,0、1 又是通過高低電平來表達的,通過與、或、非等邏輯門電路來表達二進制的數值運算,再將這些簡單的電路集成在一起,就形成了 ALU 等具有運算能力的處理器。你會看到一條指令是如何被CPU執行的,CPU 從內存或 Cache 中取出指令,放入指令寄存器,并對指令譯碼。譯碼就是按照指令的編碼規則,將指令拆分成一系列的微操作和操作數。然后發出各種設備控制指令,執行微操作。這樣就完成一條指令的執行。我們說學完編譯原理,能夠明白寫的英文代碼是如何被變成二進制指令的,學完操作系統能搞懂二進制程序是如何被鏈接在一起,又是如何被操作系統加載、執行的。而組成原理則會告訴你二進制指令是如何控制 CPU 跑起來的,我們的操作系統本質上也是一個二進制的程序。當你理解了計算機存儲層次結構,理解了多級 Cache,你就會通過優化數據訪問方式來編寫出速度更快的程序。你會學到底層體系結構對 C 這些語言的棧幀和參數傳遞的支持,參數是如何被傳遞給另外一個函數的?函數的返回值又是如何拿到。這是學習組成原理對于寫代碼的意義。學這些到底有什么意義?你會完整的看到寫的代碼如何變成二進制指令,又是如何去控制各種門電路,最后變成屏幕上花花綠綠的程序的(當然這里可能還需要學習顯示器的原理),這就是我們常說的“基礎”和“原理”。并且計算機體系結構中的很多思想,是能夠廣泛運用于現代軟件開發的,比如 CPU 的多級 Cache 思想,就是我們現在服務器開發中提高并發度常用的緩存技術,包括緩存的替換策略等等。當計算機對你不再是黑盒,你了解寫下的代碼到執行的每一步,而這也將成為你以后的核心競爭力,作為科班畢業生不應該只會使用 Java、Redis、Mysql、Spring 來寫各種網站。如果讀者里有半路轉行或者從培訓班出來的,也希望你們能夠抽出空余時間去補補這些基礎課,這會讓你在編程這條路上走的更遠和更穩。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。