在大多數開發或者準開發人員的認識中,C/C++ 是一門很是難的編程語言,不少人知道它的強大,但由於認爲「難」形成的恐懼讓不少人放棄。javascript
筆者從學生時代開始接觸 C/C++,工做之後前後擔任過 C++ 客戶端和服務器的開發經理並帶隊開發,至今已經有十多年了。雖然時至今日哪一種編程語言對我來講已經再也不重要(我目前主要從事 Java 開發),但 C/C++ 仍然是筆者最喜歡的編程語言。在我看來,C/C++ 一旦學成,其妙無窮,就像武俠小說中的「九陽神功」同樣,有了這個基礎,您能夠快速學習任何語言和編程技術。html
須要注意的是本文不細分 C 與 C++ 的區別,一般狀況下,C++ 能夠當作是 C 的一個超集,在古典時期,能夠認爲 C++ 就是C with classes。雖然現在的C++從功能層面上來看,離 C 愈來愈遠了;可是從語法層面來上來看,大多數 C++ 語法仍是與 C 基本一致的,所謂 C++ 的面向對象特性,若是細究 C++ 類方法的具體語法仍是 C 的過程式語法。固然,面向對象是一種思想,語言自己對其支持的程度當然重要,可否熟練地使用則是更要看開發者的水平了。前端
C 語言目前主要用於像操做系統這樣一類偏底層的的應用開發,包括像 Windows/linux等這樣的大型商業操做系統,和嵌入式操做系統、嵌入式設備上的應用。還有一些開源的軟件,也會選擇C開發,這些系統主要優先考慮程序執行效率和生成的可執行文件的體積(C 代碼生成的可執行文件體積相對更小),固然還有一些是歷史技術選型問題,這類軟件以redis、libevent、nginx,目前像國內的電信服務商所使用的電話呼叫系統通常也是基於一款叫 freeswitch 的開源C程序作的二次開發(項目地址:https://freeswitch.com/)。java
C++ 面向對象的語法與C相比較起來,在將高級語言翻譯成機器二進制碼的時候C++ 編譯器在背後偷偷地作了大量工做,生成了大量的額外的機器碼,而這種機器碼相對於C來講是否是必須的。例如,對於一個C++類的實例方法,編譯器在生成這個方法的機器碼時,會將函數的第一個參數設置成對象的this指針地址,以此來實現對象與函數的綁定。正由於如此,許多開發者會優化和調整編譯器生成的彙編代碼。python
咱們再來講說 C++,C++ 的應用領域目前有三大類,第一類就是咱們目前見到的各類桌面應用軟件,尤爲Windows上桌面軟件,如QQ、安全類殺毒類軟件(如金山的安全衛士,已開源,其代碼地址:http://code.ijinshan.com/sour...)、各類瀏覽器等等;另外就是一些基礎軟件和高級語言的運行時環境,如大型數據庫軟件、Java虛擬機、C#的 CLR 運行時、python編譯器和運行時環境等等;第三類就是一些業務型應用軟件的後臺,如遊戲的服務器後臺,例如魔獸世界的服務器(代碼地址:https://github.com/azerothcor...)和一些企業內部的應用系統,筆者從在某交易所從過後臺開發,其交易系統和行情繫統就是基於 C++ 開發的。linux
從上面的介紹能夠看出,與Java、python等語言相比,C/C++語言是運行在離操做系統最近的一種高級語言,所以其執行效率也比較高,可是有得必有失,也由於如此,因此C/C++這門語言存在以下特色:nginx
在應用層開發,直接使用操做系統的接口的函數,每每執行效率高,控制力度大,您的開發能力僅僅限制於操做系統自己,Java 這類語言,不少功能即便操做系統提供的,若是 Java 虛擬機不提供,開發人員也沒法使用。正如著名的編程大師 Charles Petzold 說的:git
顯而易見,究竟用哪一種方式編寫應用程序最好,其實並沒有必定之規。應用程序自己的特性應該是決定採用何種編程工具的最主要因素,可是不管未來你採用什麼樣的編程工具,經過了解操做系統 API 從而深刻理解操做系統的工做原理,這自己就有很重要的意義。操做系統是一個很是複雜的系統,在 API 之上加一層編程語言並不能消除其複雜性,最多不過是把複雜性隱藏起來而已。說不定何時,這種複雜的那一面早晚會蹦出來拖你的後腿,懂得系統 API 能讓你到時候更快地掙脫困境。在基本操做系統 API之上的任何軟件層或多或少都會限制你使用操做系統的所有功能。好比,你或許發現採用 Visual Basic 來編寫你的應用程序很是理想,可是就有那麼一兩項很是基本的功能 Visual Basic 沒法支持。每每這個時候你得非要調用基本 API 。做爲直接使用操做系統 API 的程序員,咱們的活動空間徹底由 API 來規範,再沒有什麼其餘方式比直接調用 API 更有效、更靈活多樣了程序員
總結起來,C/C++語言的開發核心是創建在直接調用操做系統 API 的基礎上的,優勢是執行效率高、發揮空間大;缺點是,須要通過系統深刻的學習,學習週期長,編寫代碼較複雜、容易出錯。github
我之因此把這一個標題單獨列出來,是想糾正如今不少 C/C++ 新人和初學者的一些的不當認識,通常有如下幾種觀點:
我相信對於80和90這一代的開發者來講,當初接觸計算機並進入軟件行業,都是從接觸 Windows 開始的。時至今日,大數據、人工智能等各類新技術方興未艾,移動互聯網如火如荼。可是不管是 linux 仍是 Windows,尤爲是Windows,仍是咱們大多數人工做、學習、娛樂使用最多的操做系統,咱們天天都會使用上運行在其上的各類軟件。咱們使用這些軟件像喝水、呼吸空氣同樣天然,因此不少人就忽視了這類軟件的「基礎做用」。對於 Windows 上的軟件開發因爲其發展了不少年了,這些領域也比較成熟,通常再也不招初中級開發,而是須要水平較高、經驗較豐富的高級開發者,這讓不少人形成了「Windows C++」開發市場需求已經很小了的錯覺。試問,PC QQ部門這些年對外招了多少人?
另外,linux C++ 和 Windows C++ 同樣,沒有孰高孰低之分,只是兩種不一樣的操做系統而已,不要以爲在linux 下敲命令就比在 Windows 的圖形化界面點擊鼠標達高級。圖形化界面之於命令行,是人們對更高級、更方便的工具的追求的必然結果。linux C++ 也不必定就是後臺開發,Windows C++ 也不必定就是客戶端開發;所謂的服務器與客戶端是個相對的概念,即誰給誰提供服務,提供服務的咱們認爲是服務端(後臺),被服務的咱們認爲是客戶端(前臺)。而 Windows 做爲後臺服務的應用也比比皆是,如筆者以前所在的某交易所的服務器後臺都是Windows下的C++程序;另外如一些遊戲類的服務器端,也很多是Windows的。
借用《UNIX編程藝術》這本書的觀點,Windows 和linux 的哲學理念不同,Windows是假設你不會操做,它教你如何操做,而 linux 是假設你會操做而後進行操做;根據這個理念,Windows 通常普通人用的多,而linux 程序員用的多。從編程的角度來講,Windows的代碼風格是使用所謂的匈牙利命名法,而linux使用的短小精悍的連字符風格,例如同一個表示屏幕尺寸的整型變量,Windows 上可能被命名爲 iScreen 或 cxScreen ,而 linux 多是 screen;再例如 Windows 上建立線程的函數叫 CreateThread,linux 下叫pthread_create。有時候,我以爲 Windows 的匈牙利命名法反而更容易理解代碼。
這裏既然提到前端(客戶端)開發和後端開發,這裏不得不提一下,這兩者沒有優劣之分。其側重點和開發思惟是不同的,前端(客戶端)開發通常有較多的界面邏輯,它們是直接與用戶打交道的,於是一款客戶端軟件的好壞很大程度上取決於其界面的易用性和流暢性,開發者只要把這一端的「一畝三分地」給管理好便可;然後端服務,對於普通用戶是透明的,開發者的程序必須儘可能體現「服務」這個字眼,即更有效地爲更多的客戶端服務,這就要求兼顧請求響應的正確性、及時性和流暢性,因爲服務軟件也是運行在某臺物理機器上的程序,鑑於CPU、內存、網絡帶寬資源有限,而服務程序通常是長週期運行的,所以必須合理的分配和使用資源(如儘可能回收再也不使用的各類資源),開發者應從全局考慮,不能在某個「客戶端」這一棵樹上「吊死」。
從我的的職業發展來看,建議從事客戶端開發的讀者適當地瞭解一下服務器開發的思路,反過來也建議從過後端開發去學習一下客戶端開發,兩者相得益彰。從我的的技術提升來講,也是頗有幫助的。例如您要學習一套開源的軟件代碼,若是您熟悉客戶端和服務器的基本開發和調試技巧,您能夠更好地學習它。而在工做上,一個項目,每每是由客戶端和服務器程序組成,若是您都熟悉,您能夠站在一個更高的角度去審視它、規劃它,這也是架構師的基本要求之一。
最後就是不少讀者關心的客戶端和服務器的薪資問題,這個沒有絕對的誰高誰低,因人而異,因能力而異,因崗位而異。
C++ 開發者有個不成文的規定就是,即便您對 C++ 很熟悉,也不要在簡歷上寫上您精通 C++,緣由很簡單—— C++ 這門語言包含的東西實在太多了,沒有人能真正「精通」全部。C++ 既支持面向對象設計(OOP),也支持以模板語法爲表明的泛型編程(GP)。並且新的 C++ 標準和遵循 C++ 新標準的編譯器也參出不窮,這些年,C++ 變化愈來愈大,愈來愈快,從最初業界和開發者翹首以盼的 C++11 標準,歷經C++1四、C++17 到今天的 C++20,這門語言與以前的版本差異愈來愈大,更多原來須要使用第三庫的功能也被陸續添加到 C++ 標準庫中。以至於C++之父 Bjarne Stroustrup 也開始對這門語言表示擔心:
在 C++11 開始的基礎建設還沒有完成,而 C++17 基本沒有在使基礎更加穩固、規範和完整方面作出改善。相反,卻增長了重要接口的複雜度,讓人們須要學習的特性數量愈來愈多。C++ 可能在這種不成熟的提議的重壓之下崩潰。咱們不該該花費大量的時間爲專家級用戶們(好比咱們本身)去建立愈來愈複雜的東西。(還要考慮普通用戶的學習曲線,越複雜的東西越不易普及。)
文章參看這裏:https://zhuanlan.zhihu.com/p/...,在Bjarne Stroustrup 的信中,他擔憂C++會像歷史的瓦薩號軍艦同樣,啓航即沉沒。
固然,咱們不用有這種擔心,畢竟咱們既不是 C++ 標準委員會成員,也不是 C++ 編譯器開發廠商。就我我的經驗來講,對於C++十一、C++1四、C++17乃至C++20,咱們學習它們的準則應該是以實用爲主,也就是說咱們應該學習其實用的部分,至於新標準提到的一些高級特性和各類複雜的模板,咱們大可沒必要去了解。咱們並非作學術研究,咱們學習 C++ 是爲了投入實際的生產開發,因此應該去學習 C++ 新標準中實用的語法和工具庫。關於C++11經常使用一些知識點,這裏也簡單地給讀者列舉一下:
auto關鍵字、for-each循環、右值及移動構造函數 + std::forward + std::move + stl容器新增的emplace_back()方法、std::thread庫、std::chrono庫、智能指針系列(std::shared_ptr/std::unique_ptr/std::weak_ptr)(智能指針的實現原理必定要知道,最好是本身實現過)、線程庫std::thread+線程同步技術庫std::mutex/std::condition_variable/std::lock_guard等、lamda表達式(JAVA中如今也經常考察lamda表達式的做用)、std::bind/std::function庫、 其餘的就是一些關鍵字的用法(override、final、delete),還有就是一些細節如能夠像JAVA同樣在類成員變量定義處給出初始化值。
這裏說的基礎不是狹義上的 C++ 語言基礎,而是包括C++開發這一輩子態體系的基礎,筆者認爲的基礎有:
說了這麼多,您可能會以爲很抽象。筆者在這裏舉個具體例子,假設咱們如今要開發一個相似電驢這樣的軟件。軟件界面以下圖:
如上圖所示,假設咱們的操做系統選擇Windows,使用語言咱們使用 C++,這就要求您必須熟悉 C++ 經常使用的語法,若是您還不熟悉,您就須要補充這方面的知識。
在熟悉 C++語法的前提下,從這款產品實現技術來看,咱們的目標產品分爲 UI 和 網絡通訊部分。下面將詳細介紹這兩部分:
對於UI部分,咱們的認識是這須要使用 Windows 的窗口技術。咱們能夠直接使用原生的 Win 32 API 來製做本身的界面庫,也能夠選擇一些咱們熟悉的界面框架,如mfc,wtl、duilib、wxWidgets等。不管您是在閱讀別人的這樣的項目仍是須要本身開發這樣的項目,在肯定了這款軟件使用的 UI 庫(或者使用原生Win 32 API),您就須要對 Windows 的窗口、對話框、消息產生、派發與處理機制須要瞭解,一樣的道理,若是不熟悉您須要補充相關的知識(關於這一點,下文再也不贅述)。
接着,根據上圖中的軟件功能,大體分爲三大模塊,即資源、下載和分享。這三大塊是可使用一個Windows Tab控件去組織,這個時候您須要瞭解 Windows Tab控件的特性。
網絡通訊部分,主要有兩大塊,第一個是程序啓動時,與服務端的交互;第二個就是文件下載與分享的 P2P 網絡。您在閱讀或開發的過程當中,若是對這些技術比較陌生,您須要補充這些知識,具體的也就是socket的各類 API函數,以及基於這些 API 邏輯的組合。固然可能也會用到操做系統平臺所特有的網絡 API 函數,如WSAAsyncSelect 網絡模型。
再一點,網絡通訊部分如何與 UI 部分進行數據交換,是使用隊列?全局變量?或者相應的 Windows 操做平臺提供的特殊通訊技術,如 PostMessage 函數、管道?若是使用隊列,多線程之間如何保持資源的一致性和解決資源競態,使用 Event、CriticalSection、Mutex、Semaphore等等?
固然,筆者這裏只列舉了這個軟件的主幹部分,還有許多方方面面的細節須要考慮。這就須要讀者根據本身的狀況,斟酌和篩選了。您想達到什麼目的,您就去學習和研究相關的代碼。
總結起來,能夠獲得以下公式:
一款C++軟件 = C++語法 + 操做系統API函數調用
若是您達到了我上面說的三點後,能夠再找一些高質量的開源的項目去實戰一下。須要注意的是最好找一些沒有複雜業務或者您熟悉其業務的開源項目(如開源的IM系統),若是你不熟悉其業務,不只要學習其業務(軟件功能),還須要再去學習它的源碼,最後可能讓咱們迷失了最初學習這款軟件的目的。
學習這些項目的同時,讀者應該先肯定本身的學習目的,若是您的目的是學習和借鑑這款軟件的架構,那麼先從總體去把握,不要一開始就迷失在細枝末節中,這類我稱之爲「粗讀」;或者,您的目的是學習下開源軟件的在一些細節上的處理與作法,這個時候,您能夠針對性地去閱讀您感興趣的模塊,深刻到每一行代碼上去。
學習開源軟件存在一種風氣,許多新手喜歡道聽途說,一聽別人說這個軟件很差,那個軟件存在某某瑕疵就放棄閱讀它的打算了。而後到了實際開發中,由於心中沒有任何已有軟件開發問題的解決方案,產生挫敗感,長此以往就對原本喜歡的 C/C++ 開發失去了興趣。學習的過程是先接觸,再熟悉,再模仿,再創造。無論什麼開源項目,在您心中沒有任何思路或者解決方案時,您應該先接觸熟悉,不斷模仿,作到至少心中有一套對於某場景的解決方案,而後再來談創新談批判、改造別人的項目。
我我的學習一套陌生的開源項目時,老是喜歡將程序用調試器正常跑起來,而後再中斷下來,統計當前的線程數目,而後經過程序入口main函數從主線程追蹤其餘工做線程是如何建立的;接着,分析和研究每一個線程的用途以及線程之間交互的,這就是總體把握,接着找我感興趣的細節去學習。
這裏我以學習 redis 爲例,將 redis 源碼從官網下載下來之後,使用您喜歡的代碼閱讀器管理起來,我這裏使用的是Visual Studio,以下圖所示:
在大體瞭解了 redis 有哪些代碼模塊之後,咱們把代碼拷貝到 linux 平臺,而後編譯並使用 gdb 調試器跑起來。以下圖所示:
!
而後使用按 ctrl + C 將gdb中斷下來,輸入info threads查看當前程序的全部線程:
接着挨個使用 thread + 線程編號 和 bt 命令去查看每一個線程的上下文調用堆棧:
而後對照每一個線程的上下文堆棧,搞清楚其邏輯,並結合主線程,看看每一個線程是在什麼時候啓動、端口在什麼時候啓動偵聽的等等。等作完這一步,關於 redis-server 的框架也基本清楚了。
接着咱們能夠選擇一個本身感興趣的命令,搞清楚redis-cli 與 redis-server命令的交互流程。
最後,若是對 redis-server 源碼中各類數據結構和細節感興趣,咱們能夠進一步深刻到具體的代碼細節。
固然,不熟悉 gdb 的讀者看筆者這段操做流程比較困難,這是正常的,說明若是想經過調試去研究 redis這一款開源軟件,您須要去補充一點 gdb 調試的知識。這就是我上文中所說的,針對性地補缺補差。
關於C++面試,面試的要求究竟是側重代碼量、項目經驗,仍是側重操做系統、數據結構這種基礎知識?我在知乎上曾經專門寫過一篇文章來介紹我面試曾經C++的面試的經歷和經驗,有興趣的讀者能夠點擊這裏查看:https://www.zhihu.com/questio...。
關於C++面試常見的面試題,能夠參考這裏:https://zhuanlan.zhihu.com/p/...,這篇文章問題點整理的很是詳細,讀者能夠參考一下。
須要注意的是,不只僅是 C++ 面試,其餘語言開發面試也是同樣。若是您是想進入大的互聯網公司的應屆生,那麼您應該優先好好準備算法和數據結構知識,以應對面試,這是大的互聯網公司面試頻率最高的考察範圍;至於其餘的基礎知識,如操做系統原理、網絡通訊等基礎知識,做爲計算機相關專業的學生,這些應該是您的專業課,若是您已經在平時的學習中掌握的很好,那就不用擔憂,這類問題通常對於應屆生求職不會問的太深;假若您還沒有學的紮實,而春招或秋招又時間臨近,沒有足夠的時間去準備這些,您應該只是儘可能去補,實在來不及也不要緊,仍是應該把重心放在好好準備算法和數據結構等知識上。
對於社會人士參加的 C++ 職位的面試,若是是大的互聯網公司,雖然社會招聘問的更多的是項目經驗,適當地爲一些基礎的算法和數據結構知識作一些準備也是很是有用的,舉個例子,若是問到二分查找這一類基礎算法,若是答不出來未免會讓面試官印象不太好,場面也比較尷尬。另外,C++是一門講究深度的編程技能,對於有必定工做年限的面試者,面試官每每會問不少原理性的細節,這就要求廣大 C++ 開發者在日常多留心、多積累、多思考技術的背後的原理。
而對於大多數小的企業,不管是應屆生仍是社會人士,只要有能力勝任必定的工做便可,因此通常只要對所面試的公司的項目有相似經驗或者相關的技術能力,基本上就可經過面試。大多數小公司在意的是您來了能不能幹活,因此這類公司對實際項目經驗和技能要求更高一點。
再者就是關於項目經驗,許多人可能以爲項目經驗必定是本身參與的項目,其實否則,項目經驗也能夠來源於您閱讀和學習其餘人的項目代碼或者開源軟件,只要您能看懂並理解它們,在面試的時候說起到,能條理清晰、自圓其說便可。固然,若是不熟悉或者只是瞭解些皮毛,切記不可信口開河、胡編亂造甚至張冠李戴,我曾經面試過一些開發者,看簡歷項目經驗豐富,實際一問的時候,只是把別人的框架或者庫拿來包裝調用一下,問及其技術原理時,不是顧左右而言他就是說不清道不明模棱兩可含糊不清,這一類人每每比不知道還讓人討厭,面試官通常反感這一類面試者的所謂的項目經驗的。
若是是學生的話,有充裕的時間,建議除了把 C++ 語法學好,系統地多讀一點基礎的書籍,如操做系統原理、網絡編程、數據結構與算法相關的各方各面的經典書籍,能夠參考下這裏:https://mp.weixin.qq.com/s/Ej...。儘可能作到等您畢業走出校園之後,至少熟悉一門編程語言和其相應的開發環境的,是一個基礎紮實、理論清晰、編碼能力強的求職者。惋惜的是,從如今的各類招聘反饋來看,大多數學生在求職時,對相關開發工具和語言的陌生程度實在讓人瞠目結舌,面試官在面試的時候會很納悶:這位學生大學四年(或者七年)究竟是否調試過程序?
社會人士因爲已經走上工做崗位,家庭、工做的雜事繁多,沒有太多的時間去系統地閱讀一些相關基礎書籍,若是您當前工做正好是從事 C/C++ 開發,那麼請結合您當前的項目來學習,搞清楚項目的體系結構、吸取項目中優秀的實現細節,針對性地補充相關知識,這是進步最快的方式。可是實際情形中,不少人以爲公司的項目代碼又爛又雜,不肯意去研究,這種思想千萬不能有的,在您沒有本身足夠好的能力給公司提供更好的解決方案,請先學習和模仿,咱們此時要保持「空杯」心態,公司的代碼再爛,它也是公司的商業價值所在;即便是純粹的業務代碼,也有它的可取之處,擇其善者而從之,其不善者而改之。尤爲是開發者處於一些初中級的開發崗位時,可能接觸不到公司核心框架的源碼,此時千萬不要盲目地去排斥。學業務,補基礎,時刻意識清醒本身所需,明白本身想要學的東西。
若是從事的不是 C++ 相關的開發,那麼能夠擠出一些時間去學習一些開源的代碼,在閱讀開源代碼的過程當中,針對性地補缺補差。不建議系統地去看《C++ primer》《UNIX環境高級編程》諸如此類的大部頭書籍,實際開發中不須要太多的這類書中的細枝末節,閱讀這類書每每只會事倍功半,甚至最後因書籍太厚、時間不夠,最後堅持不下去,最終放棄。
固然,對於社會人士,當您有必定的時間的時候必定要去補充一些基礎的、原理性的東西,千萬不要沉溺於「面向搜索引擎編程」或者「面向工資編程」,有些問題雖然當時經過搜索引擎解決了,但若是想在技術或職業上有長足的發展,必定要系統地去讀一些經典的、輕量級的書籍(如《C++對象模型》)。長期在網上的文章中尋章摘句,只會讓您的知識結構碎片化、凌亂化,甚至混亂化。並且互聯網上的技術文章質量參差不齊,有時候也容易對本身造成誤導和依賴。總而言之,做爲技術開發人員,提升本身技術水平是改變現狀、改善生活最直接的途徑。
關於C/C++,暫且就討論這麼多。最後再強調一遍,C++是一門講究深度的語言,其「深度」不是體如今會多少 C++ 語法,而是可以洞察您所寫的C++代碼背後的系統原理,這是須要長期不斷的積累的,沒有速成之法。反過來一旦學成,能夠快速地學習其餘語言和框架。我的以爲,若是自主創業或者想在二三線城市長期發展的讀者,C/C++ 應該是優選語言,有了它做爲基礎,您能夠跳出依賴各類環境和框架的窠臼,快速地學習和開發您想要的軟件,完成您想要的業務產品。
最後,限於筆者經驗水平有限,歡迎讀者就文中的觀點提出寶貴的建議和意見。若是想得到更多的學習資源或者想與我作進一步交流,也歡迎關注個人公衆號『高性能服務器開發』,一塊兒交流C/C++編程技藝與思想。也能夠加入C++開發交流羣:578019391 一塊兒交流。