干货 | 如何系统学习 C 语言?
關(guān)注、星標(biāo)公眾號(hào),直達(dá)精彩內(nèi)容
C 語(yǔ)言應(yīng)該是絕大部分同學(xué)的編程第一課。
對(duì)于非 CS 專業(yè)的同學(xué),學(xué) C 語(yǔ)言主要是掌握一些基本的編程方法,C 語(yǔ)言只是媒介。
但是對(duì)于 CS 科班的同學(xué),C 語(yǔ)言是后續(xù)的計(jì)組、體系結(jié)構(gòu)、操作系統(tǒng)、編譯原理等課程的基石,對(duì)于 C 語(yǔ)言本身甚至程序設(shè)計(jì)語(yǔ)言基礎(chǔ)原理的深入理解都是應(yīng)該掌握的。
一、一圖勝千言
如果你不想看冗余的文字,直接看我畫(huà)的這個(gè)思維導(dǎo)圖即可:
二、C 語(yǔ)言易學(xué)難精?
很多同學(xué)都反映 C 語(yǔ)言難。
實(shí)際上,相比 C++、Java 之類的更加現(xiàn)代的語(yǔ)言, C 語(yǔ)言本身的語(yǔ)法特性非常少,不像 C++ 成了一鍋大雜燴。
C 語(yǔ)言本身只包含了編程語(yǔ)言最基本的語(yǔ)法,比如變量、if、else、for、while、枚舉、結(jié)構(gòu)體等,外加一個(gè)指針。
但是為什么大家都覺(jué)得難呢?
說(shuō)下我大一時(shí)覺(jué)得難的原因吧,主要兩點(diǎn):
1. 庫(kù)太少,太原始
用 C 語(yǔ)言寫個(gè)稍微復(fù)雜點(diǎn)的項(xiàng)目,你需要上來(lái)先把數(shù)據(jù)結(jié)構(gòu)、常見(jiàn)算法先實(shí)現(xiàn)一遍,更別說(shuō)字符串,序列化這些了。
比如 Redis 里 SDS、LIST、ZSET、HASH等。
這其實(shí)對(duì)新手極其的不友好。
想做點(diǎn)有趣的東西,來(lái),先寫個(gè)鏈表?
而在 Java 里直接 New 一個(gè) LinkedLits、HashMap,簡(jiǎn)直不要太高效。
再到后來(lái),學(xué)了 Python,才發(fā)現(xiàn)這才是編程的高級(jí)形態(tài),寫個(gè)爬蟲(chóng)直接 import xx,幾句話就完事了。
而在 C 語(yǔ)言里,你需要裸寫 Socket 發(fā)網(wǎng)絡(luò)包,還要解析 HTTP 協(xié)議,還要序列化 JSON,處理 HTTPS 這些(當(dāng)然,你也可以導(dǎo)入庫(kù),但是由于 C/C++ 弱雞等同于沒(méi)有的包管理,也挺麻煩的。
這就是新手學(xué) C 容易被勸退的原因,做不出好玩的東西,全是一堆黑框框。
2. 內(nèi)存、指針
另外,常另 C 語(yǔ)言初學(xué)者感到頭疼的就是指針了。
指針其實(shí)本身不難,就是變量的地址嘛。
但是問(wèn)題在于地址是個(gè)什么東西?
理解地址,就需要理解內(nèi)存,但是鑒于大多數(shù)同學(xué)學(xué)習(xí) C 語(yǔ)言,都是在大一,那時(shí)候沒(méi)有計(jì)算機(jī)基礎(chǔ)知識(shí),其實(shí)理解起來(lái)還是稍微吃力的。
內(nèi)存,其實(shí)也很簡(jiǎn)單啊,你就把它當(dāng)做一個(gè)黑盒,提供了讀和寫的能力。
就像快遞柜一樣,提供了存東西、放東西的能力:
讀哪里?寫哪里?
當(dāng)然是需要地址啊,地址就跟快遞柜上的編號(hào)一樣,而快遞柜格子里就相對(duì)于內(nèi)存中真正存放的內(nèi)容。
記住這張快遞柜的照片,其實(shí)指針你就理解了。
什么是二級(jí)指針?
就是快遞柜格子里放的是另外一個(gè)格子的編號(hào),對(duì)應(yīng)到 C 語(yǔ)言舉個(gè)例子:
int?a?=?10; int?*pa?=?&a; int?**ppa?=?&pa;ppa 所指向的內(nèi)存存放的內(nèi)容是 pa 的地址,pa 存放的是 a 的地址(就不畫(huà)圖了,腦補(bǔ)
那你要問(wèn)了,那指針和二級(jí)指針以及更多級(jí)的指針有什么區(qū)別呢?
為什么二級(jí)指針就要兩個(gè)**?
很簡(jiǎn)單啊,不用兩個(gè) ** 的話,你怎么告訴編譯器,這個(gè)地方放的其實(shí)是另外一個(gè)內(nèi)存的地址?
這樣編譯器才能去做語(yǔ)法檢查,不然誰(shuí)知道你這是放的變量地址還是另外一個(gè)指針的地址啊。
更加詳細(xì)的內(nèi)容其實(shí)我在這篇指針的文章中講過(guò):
為什么指針被譽(yù)為 C 語(yǔ)言靈魂?
不過(guò)現(xiàn)在站在我的角度,覺(jué)得指針很簡(jiǎn)單,似乎很好理解,也許是知識(shí)詛咒的原因。
知識(shí)詛咒:指的就是一旦我們自己知道某樣?xùn)|西,我們就會(huì)發(fā)現(xiàn)很難想象不知道它的時(shí)候會(huì)是什么樣子。
后來(lái)接觸到 Java,如同發(fā)現(xiàn)了新大陸一般。
在 C 語(yǔ)言里,申請(qǐng)一個(gè)內(nèi)存,你得隨時(shí)記得在合適的地方釋放。
釋放得不合適?對(duì)不起 coredump 隨時(shí)等著你。
忘了釋放?對(duì)不起,內(nèi)存泄露等著你~
亂寫指針?對(duì)不起,內(nèi)存越界等著你~
棧內(nèi)存越界?VS下,燙燙燙等著你~
這些東西想必是每一位 C Programmer 的家常便飯了。
而在 Java 這類更加高級(jí)語(yǔ)言中,直接 new 一個(gè)就行了。
好了,扯淡完畢,在這里給出一條系統(tǒng)化學(xué)習(xí) C 語(yǔ)言的路線吧:
第一階段:初學(xué)少看書(shū),多看視頻
這真的是血淚教訓(xùn),我大一會(huì)傻乎乎的去把課本看一遍,然后做課本后面編程練習(xí)題。
學(xué)得那叫一個(gè)艱難呀,大家都知道,課本為了保留其嚴(yán)謹(jǐn)性和全面性,往往都具有一個(gè)特點(diǎn),那就是「不說(shuō)人話」。
對(duì)于 C 語(yǔ)言,初學(xué)者有一個(gè)難點(diǎn)需要克服,就是容易遇到各種編譯、鏈接錯(cuò)誤,而且不知道怎么解決:
比如這種很初級(jí)的報(bào)錯(cuò),但是初學(xué)者往往看到就慌了。
遇到這種情況,我的建議是仔細(xì)讀報(bào)錯(cuò)的提示,解決方法往往就在這些 note 中,如果實(shí)在解決不了,就復(fù)制 note 去 Google 上搜。
這個(gè)階段你需要三份資料:
一是補(bǔ) CS 基礎(chǔ)概念,也就是計(jì)算機(jī)導(dǎo)論
二是一本 C 語(yǔ)言的好書(shū)。
三是一個(gè)優(yōu)質(zhì)的 C 語(yǔ)言視頻
對(duì)于每一點(diǎn),我都只推薦一份我覺(jué)得最合適的,避免你陷入選擇困難癥:
計(jì)算機(jī)導(dǎo)論
CS 50,這是哈佛的一門計(jì)算機(jī)入門神課,忘了是在大一下還是大二看的了,當(dāng)時(shí)覺(jué)得如獲至寶。鏈接如下:
https://cs50.harvard.edu/college/2021/spring
讓我現(xiàn)在還記憶尤新的一個(gè)點(diǎn),是這個(gè)老師講到 binary search 的時(shí)候,直接舉了查字典這個(gè)過(guò)程,然后現(xiàn)場(chǎng)手撕字典。。。
另外,這門課是用 C 語(yǔ)言作為教學(xué)語(yǔ)言的,非常難得。
因?yàn)楝F(xiàn)在國(guó)外很多 CS 名校的導(dǎo)論課都會(huì)采用類似 Python、Schema 這樣的語(yǔ)言。
但其實(shí)你會(huì)發(fā)現(xiàn),在 CS 50,不會(huì)講多少 C 語(yǔ)言的語(yǔ)法,C 語(yǔ)言只是作為一種傳遞編程思想、引領(lǐng)你入門計(jì)算機(jī)的媒介。
而不是在教 C 語(yǔ)言。
C 語(yǔ)言視頻
不多說(shuō)了,推薦一個(gè)我以前在地鐵都在刷的,浙大翁凱老師教授的,直接去中國(guó)大學(xué) MOOC 搜索即可,這是鏈接:
https://www.icourse163.org/course/ZJU-199001
說(shuō)到這,我不得不多說(shuō)兩句翁凱老師了,我看了好幾門他的課,最直觀的感受是:
講解透徹、聲音好聽(tīng),而且特別注重細(xì)節(jié),貼一張浙大匿名教室系統(tǒng)關(guān)于翁凱老師的評(píng)價(jià)吧:
一句話,翁凱老師是真的想把計(jì)算機(jī)內(nèi)部原理、編程語(yǔ)言以深入淺出的方式教給大家。
C語(yǔ)言書(shū)
《C程序設(shè)計(jì)語(yǔ)言》,一本被稱作 C 語(yǔ)言圣經(jīng)的書(shū)。
在短小的篇幅中,涵蓋了 <stdio.h>、<string.h> 中的大部分例子,習(xí)題也都是經(jīng)典,從 hello world 開(kāi)始,到二分查找、二叉樹(shù)、快排、哈希表等,甚至還用遞歸下降寫了詞法分析,幫你理解復(fù)雜的聲明。
不過(guò)要看懂里面的每一個(gè)例子,也是有一定難度的,需要有一點(diǎn)點(diǎn)基礎(chǔ),但是不妨礙作為入門書(shū),可以多看幾遍。
對(duì)了,一直說(shuō)學(xué)編程要多寫,但是很多初學(xué)者也不知道寫什么。
在這里,我給出一些學(xué)完 C 語(yǔ)言基本語(yǔ)法后,可以練手的例子:
鏈表
就用 C 語(yǔ)言把鏈表翻來(lái)覆去的寫,從最基本的鏈表插入、刪除、單向、雙向、環(huán)裝鏈表。
到鏈表反轉(zhuǎn)、合并、分割等。
別看基礎(chǔ),但是很多同學(xué)到大四了,可能都寫不對(duì),這里考察是否足夠細(xì)心、邏輯是否縝密。
會(huì)不會(huì)操作著就把鏈給斷了。
寫點(diǎn)小東西
比如圖書(shū)管理系統(tǒng)、俄羅斯方塊、貪吃蛇之類。
這種代碼量大多在 500 - 1000,會(huì)綜合運(yùn)用函數(shù)、文件操作、動(dòng)態(tài)內(nèi)存、指針這些關(guān)鍵的東西。
第二階段:搞懂內(nèi)存,看書(shū)、寫代碼
這個(gè)階段,是需要掌握一些計(jì)算機(jī)系統(tǒng)知識(shí)才能學(xué)好的,比如虛擬地址就和操作系統(tǒng)相關(guān)了,而函數(shù)調(diào)用棧這些又和匯編相關(guān)。
又比如很多學(xué)了很久的同學(xué),還不太清楚變量的聲明和定義區(qū)別,extern 又有什么作用,這些實(shí)際上就需要理解內(nèi)存分區(qū)的東西。
C 語(yǔ)言的核心就在于指針、內(nèi)存,能不能學(xué)好、用好 C 語(yǔ)言,更多在于是否擁有扎實(shí)的計(jì)算機(jī)結(jié)構(gòu)、存儲(chǔ)、運(yùn)算原理方面的知識(shí)。
所以強(qiáng)烈建議在學(xué)習(xí) C 語(yǔ)言的同時(shí)去了解一下補(bǔ)碼、數(shù)的二進(jìn)制表示、內(nèi)存、匯編等知識(shí),尤其是內(nèi)存和匯編,這兩個(gè)對(duì)于深刻理解指針和熟練運(yùn)用有很大的幫助。
我就不仔細(xì)介紹了,要介紹的書(shū)都在這張思維導(dǎo)圖里了。
深入學(xué)習(xí)指針
《C和指針》、《深入理解C指針》,真的是涉及指針?lè)椒矫婷娴暮脮?shū),墻裂推薦。
編譯、鏈接
這倆兄弟懂了其實(shí)也不能幫你提高寫代碼的水平,主要是對(duì)于一些編譯、鏈接過(guò)程的報(bào)錯(cuò),更加的明確,不至于懵。
比如鏈接過(guò)程中常見(jiàn)的錯(cuò)誤是符號(hào)未找到(undefined reference)和符號(hào)重定義(redefinition)
當(dāng)你熟悉鏈接過(guò)程,符號(hào)查找過(guò)程之后,解決對(duì)應(yīng)的報(bào)錯(cuò)也會(huì)得心應(yīng)手。
匯編
C 語(yǔ)言之下就是匯編,會(huì)匯編,你就能直接把 C 語(yǔ)言衣服扒掉,看看背后的實(shí)現(xiàn),比如大家都在討論數(shù)組和指針有什么區(qū)別?
你去寫個(gè)程序,然后 gcc -S 一下,看下匯編代碼,你就會(huì)發(fā)現(xiàn)沒(méi)啥區(qū)別。。。
匯編不需要會(huì)寫,會(huì)看懂部分記得,大可不必去刻意的記住各種指令、尋址方式。
好了,今天的 C 語(yǔ)言學(xué)習(xí)之路就到這里了,其實(shí)還有很多東西沒(méi)提,比如 Linux C 方向的。
主要為了突出學(xué)習(xí) C 語(yǔ)言本身,不想喧賓奪主,讓大家迷失了方向。
另外,我推薦的這些書(shū)籍,我也都匯總了:
這一套學(xué)完,C 語(yǔ)言基本問(wèn)題不大。
有需要的可以點(diǎn)擊下方公眾號(hào),關(guān)注后回復(fù)「C語(yǔ)言」即可。
祝大家周末愉快~~
總結(jié)
以上是生活随笔為你收集整理的干货 | 如何系统学习 C 语言?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 华为p20pro和p30pro区别
- 下一篇: 绝对好文:嵌入式系统的软件架构设计!