為什么突然想寫這個話題呢?
最近有不少留學(xué)生在問:大學(xué)學(xué) Java 和 C++ 哪個好找工作,學(xué)前端好還是后端好,該學(xué) Vue 還是 React。。。
我也曾深刻體會過留學(xué)壓力。回想剛踏上他國之時,操著不太熟練的外文,卻要努力聽懂著帶口音的教授俚語發(fā)音,學(xué)習(xí)著晦澀難懂的CS,Math,Economic。獨自漂泊海外,深夜趕due的苦楚卻無人能助,多希望有個學(xué)長能點撥相助。
所以畢業(yè)后我成立了三洋公司三洋公司,團(tuán)隊由最初的三人,經(jīng)過9年發(fā)展,已經(jīng)形成了4000人的導(dǎo)師團(tuán)隊規(guī)模。導(dǎo)師團(tuán)隊由FLAG工程師、在美高GPA碩博士、國內(nèi)985已發(fā)SCI論文的碩博、大陸阿里騰訊等頭部互聯(lián)網(wǎng)工程師組成。
編程真的是一門需要靠興趣和一點點天賦才能學(xué)好的技能。初學(xué)者難免對編程思路有困惑無從下手。我最初學(xué)習(xí)之時,發(fā)現(xiàn)讀大牛寫的代碼都會受益頗多,這也是為什么即使在職的工程師,公司也推薦他們在github讀開源系統(tǒng)的代碼。我們的服務(wù)也是給大家提供一個讀代碼的平臺,專業(yè)的導(dǎo)師為您的課業(yè)寫了優(yōu)質(zhì)的代碼,您一定要認(rèn)真去看,最好能動手調(diào)試,這樣體會更深。
在這里,我粗淺的把計算機(jī)編程領(lǐng)域的知識分為兩個部分:
基礎(chǔ)知識
特定領(lǐng)域知識
基礎(chǔ)知識是指不管從事任何方向的軟件工程師都應(yīng)該掌握的,比如數(shù)據(jù)結(jié)構(gòu)、算法、操作系統(tǒng)。
特定領(lǐng)域知識就是你從事某個細(xì)分方向時需要掌握的知識,比如做游戲引擎的需要掌握圖形學(xué);做前端的需要掌握瀏覽器渲染原理、前端三大件;算法工程師需要更多的數(shù)學(xué)知識。
畢竟計算機(jī)各種門類挺多的,需要選個細(xì)分方向?qū)Q邢氯ィ裁炊紝W(xué)只會什么都不精(大佬除外啦。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 一、基礎(chǔ)知識
現(xiàn)在大環(huán)境比較浮躁,很少有人愿意花心思在基礎(chǔ)上,喜歡直接學(xué) Python 搞機(jī)器學(xué)習(xí)、寫秒殺、做商城。
找工作的時候都是想看面經(jīng)、總結(jié)速成。
但是作為優(yōu)秀的計算機(jī)系學(xué)生的你怎么能流于各種編程框架(造框架除外),糾結(jié)學(xué) SpringBoot 還是 SSH 呢?
把時間花在算法、基礎(chǔ)學(xué)科上他不香嗎?功利一點講,回報反而會更大。
況且在計算機(jī)領(lǐng)域,很多基礎(chǔ)的理論并不十分高深,我們努努力就可以掌握其中的核心知識。
1.1 數(shù)學(xué)
首先說明,這里把數(shù)學(xué)列出來不是為了顯得高端,而是自己吃過數(shù)學(xué)的虧。
如果你是自學(xué)轉(zhuǎn)行當(dāng)程序員,我當(dāng)然不會推薦數(shù)學(xué),因為轉(zhuǎn)行的大概率是去學(xué) Java、前端這類,對數(shù)學(xué)基本沒啥要求。
但是這篇文章主要面向的是還在大學(xué)的科班學(xué)生,這部分同學(xué)以后也許會去做算法(CV、NLP之類)、游戲引擎、信息安全編碼等
這些方向?qū)?shù)學(xué)要求就會偏高,在計算機(jī)領(lǐng)域,線代、概率論、統(tǒng)計學(xué)這些數(shù)學(xué)分支相對比較重要,計算機(jī)本質(zhì)上還是離散的。
比如在機(jī)器學(xué)習(xí)或數(shù)據(jù)挖掘中常常用線性代數(shù)來降低數(shù)據(jù)維度,很多問題最終都能化為求解線性方程組。
所以為了避免以后想走這些方向卻被數(shù)學(xué)卡住,在大一、大二上數(shù)學(xué)課的時候就好好的學(xué)一下。
書到用時方恨少,不要現(xiàn)在以為沒用處就不好好學(xué),等你需要的時候,就知道后悔了。(默默流下了不學(xué)無術(shù)的眼淚┭┮﹏┭┮
什么?你說以后肯定做開發(fā)方向?
那的確可以把數(shù)學(xué)優(yōu)先級放后面一點,用得確實不多,不過上數(shù)學(xué)課的時候總該認(rèn)真聽下吧,拿個高績點也是百利無一害嘛。說不準(zhǔn)哪天你又想加入算法內(nèi)卷大軍呢?
1.2 C語言
你也許會很疑惑,這里明明說基礎(chǔ)知識,為什么要把一門編程語言單獨列出來呢?
因為在我看來,沒有比 C 語言更適合用來理解計算機(jī)系統(tǒng)了。
我們后面將會提到的操作系統(tǒng)、體系結(jié)構(gòu) 這些東西非常適合用 C 語言去理解或者去實踐。
并且 C 語言本身的語言特性非常少,但是想學(xué)好又是不容易,很多人都覺得 C 語言難,難在哪里呢?回想了一下我大一時的感受:
簡陋的標(biāo)準(zhǔn)庫,幾乎沒有可用的數(shù)據(jù)結(jié)構(gòu)和算法,什么都得自己來
指針很難理解和使用
需要了解匯編、鏈接、裝載、內(nèi)存等才能把 C 語言用好
不巧的是,這些東西正是計算機(jī)系統(tǒng)知識的一部分,所以用 C 語言作為學(xué)習(xí)計算機(jī)系統(tǒng)知識是最有效率的方式。
真的很難想象用 Java 或是 Python 去給別人講解內(nèi)存,因為這些語言抽象程度都比 C 語言高,意味著離計算機(jī)系統(tǒng)也就越遠(yuǎn)。
在 TIOBE 編程語言排行榜上,C語言幾乎永遠(yuǎn)占據(jù)前三位,其地位自然毋庸置疑。
而且?guī)缀跄汩_發(fā)中用到的很多東西都是用C語言編寫的,Linux、Nginx、Redis、MySQL、Git......或許你會想要探究下原理,閱讀點這些開源軟件的源碼,那么 C 語言也是你必備的瑞士軍刀。
深入學(xué)習(xí) C 語言,能夠了解計算機(jī)底層的執(zhí)行原理,是理解程序運行機(jī)制的絕佳語言,無出其右。
在這里,不得不引用對C語言最經(jīng)典的總結(jié):
任何比C語言更低級的語言,都不足以完整地抽象一個計算機(jī)系統(tǒng);任何比C高級的語言,都可以用C來實現(xiàn)。
這真是極高而中肯的評價!
所以對于計算機(jī)科班來說,不管你是做前端還是后端,算法還是開發(fā),C 語言都建議你好好學(xué)習(xí)。這是無關(guān)方向的一門語言,就是基礎(chǔ)!
1.3 操作系統(tǒng)
我們編程的 IDE、寫出來的程序全部都需要運行在操作系統(tǒng)上,說操作系統(tǒng)是計算機(jī)軟件的基石也不為過。
程序運行起來就需要創(chuàng)建進(jìn)程,這涉及到操作系統(tǒng)的進(jìn)程管理;寫程序需要定義變量、存儲數(shù)據(jù)吧,這又涉及到內(nèi)存,對應(yīng)內(nèi)存管理;有時候我們還需要讀寫文件,這又離不開和文件系統(tǒng)打交道;你需要學(xué)習(xí)使用鎖、條件變量、臨界區(qū)來保證程序并發(fā)執(zhí)行時不會錯亂。
而讀寫文件、分配內(nèi)存這些又離不開系統(tǒng)調(diào)用(System call)。
并且當(dāng)你真正做起工程就會發(fā)現(xiàn),很多問題是和操作系統(tǒng)緊密相關(guān)的,不理解操作系統(tǒng),你連問題的原因都分析不出來。
1.4 編譯原理
編譯原理可能是我們平時接觸得最少的了,大家也許會覺得自己又不用去造新的編程語言,學(xué)編譯原理干啥。
學(xué)好編譯原理有啥用?
你會站在更高的角度去審視這些編程語言,看到的不再是表面的語法,更會想到語法背后的實現(xiàn)。
這種感覺很透徹,就像搞懂了操作系統(tǒng)、體系結(jié)構(gòu)你會明白一個程序從雙擊鼠標(biāo)開始,到底是如何被運行起來的,這種掌握一切細(xì)節(jié),透徹的感覺,真的很奇妙,不信你去試試。
說人話!
那學(xué)了編譯原理你能干啥?
當(dāng)你學(xué)完有限狀態(tài)機(jī)以后,你會發(fā)現(xiàn)以前覺得很牛逼正則表達(dá)式似乎自己也能用 DFA、NFA 實現(xiàn)一下了。狀態(tài)機(jī)的思想在編程中很多地方都用得上。
比如解析 HTTP 協(xié)議,如果沒學(xué)過狀態(tài)機(jī)思想,你可能會一行行的 if/else 去做解析,這里最麻煩的地方在于,if/else 需要提前將 HTTP 頭部字段都接收到再來判斷,而我們知道 HTTP 基于 TCP,而 TCP 是流式傳輸,所以你很有可能是幾個字符一組組接收到的,這個時候用 if/else 寫出來就很難看了。
而用狀態(tài)機(jī)編寫起來代碼就會非常優(yōu)雅。狀態(tài)的轉(zhuǎn)移是由規(guī)則驅(qū)動的,接收到一個字符就判斷一個,非常的方便。
繼續(xù)學(xué)完語法分析,你會掌握遞歸下降分析這樣非常重要的思想,你可以使用遞歸下降快速的實現(xiàn)四則運算計算器。
如果不用遞歸下降你可能需要先中綴表達(dá)式轉(zhuǎn)后綴,然后求值,這是我們大一數(shù)據(jù)結(jié)構(gòu)課寫的,當(dāng)時用棧寫的,有點麻煩。后來學(xué)完編譯原理,又用遞歸下降重寫了一遍,區(qū)區(qū)幾十行代碼遍搞定。
還有一類場景在實際開發(fā)中的用的很多,比如淘寶、京東這樣的電商,它們的營銷規(guī)則有很多,比如滿減、直減、跨店等等,這樣的規(guī)則是不可能寫死在代碼里的。
那是怎么做的呢?
一般會實現(xiàn)一個配置系統(tǒng),并設(shè)計一個DSL(領(lǐng)域特定語言)來表達(dá)這些規(guī)則,將規(guī)則直接配置到系統(tǒng)中,這樣可以非常方便的修改,那么如何在代碼里去解析 DSL 定義的規(guī)則呢?這就需要為 DSL 寫一個語法解析器,這里就會用到語法分析的方法。
DSL(Domain Specific Language),是一種用于某個特定領(lǐng)域的程序設(shè)計語言。這種特定于某個領(lǐng)域是相對于 C、C++、Python 這種通用語言而言的,通用語言可以在各個領(lǐng)域使用,我們熟悉的大多數(shù)程序設(shè)計語言都是通用語言,它們都是圖靈完備的。
像我們平常經(jīng)常使用的 JSON、SQL、HTML 這些都算是一種 DSL,你甚至可以嘗試用遞歸下降去寫一個 JSON、XML 解析器,這比寫電商網(wǎng)站更有價值的。
繼續(xù)往下學(xué)你會了解到抽象語法樹 AST 如何生成、如何轉(zhuǎn)化為中間代碼、如何對中間代碼優(yōu)化、最終又是怎么生成機(jī)器指令的。
你會看到貪心算法在寄存器分配中的應(yīng)用,也會看到圖論中的可達(dá)性分析又是如何實現(xiàn)死代碼消除。
IDE上面那個綠色的編譯按鈕對你不再是黑魔法。
為啥點一下就能生成可執(zhí)行的程序?
你寫的英文字母又是如何變成一個個二進(jìn)制指令的?
學(xué)完編譯原理,這些通通不是問題,媽媽再也不用擔(dān)心你的學(xué)習(xí)~
當(dāng)然完成一個像 GCC、Clang 這樣的編譯器難度太高太高,我們學(xué)習(xí)編譯原理的目的也不是去造這樣的輪子,而是為了更好的理解和運用編程語言。
1.5 體系結(jié)構(gòu)&組成原理
上面說的都是軟件層面,體系結(jié)構(gòu)則是關(guān)于計算機(jī)是如何工作的,你會了解到典型的存儲程序計算機(jī)是怎樣運轉(zhuǎn)的。
記得南大有個老師說過 “我們不是學(xué)習(xí)使用計算機(jī)的,而是學(xué)習(xí)如何造計算機(jī)”,雖然造計算機(jī)有點夸張,但是至少我們得了解下計算機(jī)的實現(xiàn)原理,了解下代碼是怎么被 CPU 執(zhí)行的吧?不然其實你會很困惑,明明一堆英文字母,怎么在 CPU 這種電路上跑起來的,我大一學(xué) C 語言就百思不得其解,直到后來學(xué)了組成原理和數(shù)字邏輯。
我們說計算機(jī)中一切都是 0、1,0、1 又是通過高低電平來表達(dá)的,通過與、或、非等邏輯門電路來表達(dá)二進(jìn)制的數(shù)值運算,再將這些簡單的電路集成在一起,就形成了 ALU 等具有運算能力的處理器。
你會看到一條指令是如何被CPU執(zhí)行的,CPU 從內(nèi)存或 Cache 中取出指令,放入指令寄存器,并對指令譯碼。譯碼就是按照指令的編碼規(guī)則,將指令拆分成一系列的微操作和操作數(shù)。然后發(fā)出各種設(shè)備控制指令,執(zhí)行微操作。這樣就完成一條指令的執(zhí)行。
我們說學(xué)完編譯原理,能夠明白寫的英文代碼是如何被變成二進(jìn)制指令的,學(xué)完操作系統(tǒng)能搞懂二進(jìn)制程序是如何被鏈接在一起,又是如何被操作系統(tǒng)加載、執(zhí)行的。而組成原理則會告訴你二進(jìn)制指令是如何控制 CPU 跑起來的,我們的操作系統(tǒng)本質(zhì)上也是一個二進(jìn)制的程序。
當(dāng)你理解了計算機(jī)存儲層次結(jié)構(gòu),理解了多級 Cache,你就會通過優(yōu)化數(shù)據(jù)訪問方式來編寫出速度更快的程序。
你會學(xué)到底層體系結(jié)構(gòu)對 C 這些語言的棧幀和參數(shù)傳遞的支持,參數(shù)是如何被傳遞給另外一個函數(shù)的?函數(shù)的返回值又是如何拿到。
這是學(xué)習(xí)組成原理對于寫代碼的意義。
1.6 數(shù)據(jù)結(jié)構(gòu)與算法
為什么把算法放到最后來講,是不重要嗎?相反,它太重要了,所以才讓它來壓軸。
如果要問我大學(xué)什么最后悔?那肯定是沒有從大一就開始好好學(xué)算法,去打 ACM。
現(xiàn)在還在大一、大二的同學(xué)還不抓緊機(jī)會,別等到以后來后悔。當(dāng)然,不打 ACM,我們也是能夠?qū)W好數(shù)據(jù)結(jié)構(gòu)和算法的。
數(shù)據(jù)結(jié)構(gòu)和算法你能在任何計算機(jī)領(lǐng)域里看到,比如在編譯原理中寄存器的分配會用到貪心,死代碼檢測與消除會用到圖論里不可達(dá)的知識;操作系統(tǒng)進(jìn)程、線程調(diào)度會用到多級隊列和調(diào)度算法;組成原理中 Cache 的替換會用到 LRU、FIFO 等算法;開發(fā)必備的數(shù)據(jù)庫也離不開 B+ 樹、LSM 等數(shù)據(jù)結(jié)構(gòu)和查找算法。
很多時候我們需要的算法都被封裝到編程語言的基礎(chǔ)庫里了,以至于很多同學(xué)會覺得算法離我們太遠(yuǎn),其實不是的。
如果不學(xué)習(xí)算法,連什么時候用 Map(紅黑樹實現(xiàn))、什么時候用 HashMap 都分不清。
所以學(xué)習(xí)算法有助于我們根據(jù)應(yīng)用場景選擇最合適的數(shù)據(jù)結(jié)構(gòu)。
日常開發(fā)中也一定離不開算法,比如小北最近工作中涉及的某種嵌套 TLV(Tag-Length-Value)結(jié)構(gòu)編碼的解析,就需要用到遞歸、多叉樹等知識。如果不學(xué)習(xí)算法,那么程序中只能見到大量的 if/else、while/for。。。
可以說不會算法的工程師一定不是一個優(yōu)秀的工程師。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 二、領(lǐng)域知識
這個我不敢說太多,因為各個領(lǐng)域我也不太懂。只簡單提一點,拋磚引玉罷了。
如果你想去騰訊、網(wǎng)易做游戲引擎開發(fā),那么圖形學(xué)一定是你繞不開的知識,此外你還得學(xué)習(xí)渲染管線、著色器、物理、光照等等。
如果你想去 PingCap 這樣的公司做分布式存儲,那么分布式理論知識一定是你繞不開的關(guān)口,包括 CAP 定理、Paxos 算法、Raft 算法、ZAB 協(xié)議等等。
如果你想寫一個數(shù)據(jù)庫,那么你需要去了解磁盤、索引實現(xiàn)、SQL 解析(編譯原理)、事務(wù)、如何用 MVCC 解決讀寫沖突等等一大堆的東西,還得了解一大堆編程語言層面的東西,比如鎖、信號量、并發(fā)編程技巧,不得不說造數(shù)據(jù)庫是一個臟活也是一個累活。
更進(jìn)一步你想去做分布式數(shù)據(jù)庫,那可能還得去學(xué)習(xí)數(shù)據(jù)分片的知識,查詢?nèi)蝿?wù)如何做,是集中做,還是將邏輯下推給各個節(jié)點,如何實現(xiàn)分布式事務(wù)等等。
你說你只想去大廠 CRUD?沒毛病,老鐵!
那你得熟悉一門編譯型語言(C/C++、Java、Go),理解語言部分底層原理,比如 C++ 你得看看 STL、看看對象模型吧,你不懂什么虛函數(shù)表、智能指針還想去騰訊寫 C++? Java 的你得背背 JVM,什么垃圾回收算法吧,你不看看ConcurrentHashMap 好意思說你是做Java的?
咱CRUD的對象是數(shù)據(jù)庫吧?那不得學(xué)學(xué)怎么才能把數(shù)據(jù)庫用好。用戶通過 HTTP 訪問我們得服務(wù),總得了解 HTTP吧?順帶著不看下 TCP 三次握手、四次揮手你好意思說是學(xué)計算機(jī)的?
用戶把錢、信息放咱們這,總得保證用戶數(shù)據(jù)安全吧?那 XSS、SQL 注入、CSRF 這些常見的 Web 攻擊手段你總得了解吧?HTTPS、RSA、簽名、數(shù)字證書這些安全手段總得知道吧。
雙十一流量太大,老板還讓你必須頂住,那你總得了解下緩存、異步、消息隊列、NoSQL 這些千萬 QPS 必備的大殺器吧?
看看!要想做好CRUD也不是那么容易滴。
(上面這段只是換一種方式把做后端的同學(xué)要學(xué)的知識寫出來,不是吐槽更不是調(diào)侃,純屬娛樂。