【編程學(xué)習(xí)筆記】如何組織構(gòu)建多文件 C 語言程序法绵!編程也有~

優(yōu)秀 Unix 程序哲學(xué)

? ? ? ?首先转质,你要知道這個 C 程序是一個?Unix?命令行工具。這意味著它運行在(或者可被移植到)那些提供 Unix C 運行環(huán)境的操作系統(tǒng)中叼风。當(dāng)貝爾實驗室發(fā)明 Unix 后取董,它從一開始便充滿了設(shè)計哲學(xué)。

? ? ? ?用我自己的話來說就是:程序只做一件事无宿,并做好它茵汰,并且對文件進行一些操作。雖然“只做一件事孽鸡,并做好它”是有意義的蹂午,但是“對文件進行一些操作”的部分似乎有點兒不合適。

? ? ? ?事實證明彬碱,Unix 中抽象的 “文件” 非常強大豆胸。一個 Unix 文件是以文件結(jié)束符(EOF)標志為結(jié)尾的字節(jié)流,僅此而已巷疼。

? ? ? ?文件中任何其它結(jié)構(gòu)均由應(yīng)用程序所施加而非操作系統(tǒng)晚胡。操作系統(tǒng)提供了系統(tǒng)調(diào)用,使得程序能夠?qū)ξ募?zhí)行一套標準的操作:打開、讀取估盘、寫入瓷患、尋址和關(guān)閉(還有其他,但說起來那就復(fù)雜了)遣妥。

? ? ? ?對于文件的標準化訪問使得不同的程序共用相同的抽象擅编,而且可以一同工作,即使它們是不同的人用不同語言編寫的程序箫踩。

? ? ? ?具有共享的文件接口使得構(gòu)建可組合的的程序成為可能爱态。一個程序的輸出可以作為另一個程序的輸入。

? ? ? ?Unix 家族的操作系統(tǒng)默認在執(zhí)行程序時提供了三個文件:標準輸入(stdin)班套、標準輸出(stdout)和標準錯誤(stderr)肢藐。其中兩個文件是只寫的:stdout?和?stderr。而?stdin是只讀的吱韭。當(dāng)我們在常見的 Shell 比如 Bash 中使用文件重定向時,可以看到其效果鱼的。

$ ls | grep foo | sed -e 's/bar/baz/g' > ack

? ? ? ?這條指令可以被簡要地描述為:ls?的結(jié)果被寫入標準輸出理盆,它重定向到?grep?的標準輸入,grep?的標準輸出重定向到?sed?的標準輸入凑阶,sed?的標準輸出重定向到當(dāng)前目錄下文件名為?ack?的文件中猿规。

? ? ? ?我們希望我們的程序在這個靈活又出色的生態(tài)系統(tǒng)中運作良好,因此讓我們編寫一個可以讀寫文件的程序宙橱。


喵嗚喵嗚:流編碼器/解碼器概念

? ? ? ?當(dāng)我還是一個露著豁牙的孩子懵懵懂懂地學(xué)習(xí)計算機科學(xué)時姨俩,學(xué)過很多編碼方案。它們中的有些用于壓縮文件师郑,有些用于打包文件环葵,另一些毫無用處因此顯得十分愚蠢。

? ? ? ?為了讓我們的程序有個用途宝冕,我為它更新了一個?21 世紀?的概念张遭,并且實現(xiàn)了一個名為“喵嗚喵嗚” 的編碼方案的概念(畢竟網(wǎng)上大家都喜歡貓)。這里的基本的思路是獲取文件并且使用文本 “meow” 對每個半字節(jié)(半個字節(jié))進行編碼地梨。小寫字母代表 0菊卷,大寫字母代表 1。因為它會將 4 個比特替換為 32 個比特宝剖,因此會擴大文件的大小洁闰。沒錯,這毫無意義万细。但是想象一下人們看到經(jīng)過這樣編碼后的驚訝表情扑眉。

$ cat /home/your_sibling/.super_secret_journal_of_my_innermost_thoughts

MeOWmeOWmeowMEoW...

? ? ? ?這非常棒。


最終的實現(xiàn)

? ? ? ?目的是說明如何組織構(gòu)建多文件 C 語言程序。

? ? ? ?既然已經(jīng)確定了要編寫一個編碼和解碼“喵嗚喵嗚”格式的文件的程序時襟雷,我在 Shell 中執(zhí)行了以下的命令 :

$ mkdir meowmeow

$ cd meowmeow

$ git init

$ touch Makefile ? ? # 編譯程序的方法

$ touch main.c ? ? ? # 處理命令行選項

$ touch main.h ? ? ? # “全局”常量和定義

$ touch mmencode.c ? # 實現(xiàn)對喵嗚喵嗚文件的編碼

$ touch mmencode.h ? # 描述編碼 API

$ touch mmdecode.c ? # 實現(xiàn)對喵嗚喵嗚文件的解碼

$ touch mmdecode.h ? # 描述解碼 API

$ touch table.h ? ? ?# 定義編碼查找表

$ touch .gitignore ? # 這個文件中的文件名會被 git 忽略

$ git add .

$ git commit -m "initial commit of empty files"

? ? ? ?簡單的說刃滓,我創(chuàng)建了一個目錄,里面全是空文件耸弄,并且提交到 git咧虎。

? ? ? ?即使這些文件中沒有內(nèi)容,你依舊可以從它的文件名推斷每個文件的用途计呈。為了避免萬一你無法理解砰诵,我在每條?touch?命令后面進行了簡單描述。

? ? ? ?通常捌显,程序從一個簡單?main.c?文件開始茁彭,只有兩三個解決問題的函數(shù)。然后程序員輕率地向自己的朋友或者老板展示了該程序扶歪,然后為了支持所有新的“功能”和“需求”理肺,文件中的函數(shù)數(shù)量就迅速爆開了∩屏“程序俱樂部”的第一條規(guī)則便是不要談?wù)摗俺绦蚓銟凡俊泵萌诙l規(guī)則是盡量減少單個文件中的函數(shù)。

? ? ? ?老實說炫欺,C 編譯器并不關(guān)心程序中的所有函數(shù)是否都在一個文件中乎完。但是我們并不是為計算機或編譯器寫程序,我們是為其他人(有時也包括我們)去寫程序的品洛。我知道這可能有些奇怪树姨,但這就是事實。程序體現(xiàn)了計算機解決問題所采用的一組算法桥状,當(dāng)問題的參數(shù)發(fā)生了意料之外的變化時帽揪,保證人們可以理解它們是非常重要的。當(dāng)在人們修改程序時岛宦,發(fā)現(xiàn)一個文件中有 2049 函數(shù)時他們會詛咒你的台丛。

? ? ? ?因此,優(yōu)秀的程序員會將函數(shù)分隔開砾肺,將相似的函數(shù)分組到不同的文件中挽霉。這里我用了三個文件?main.c、mmencode.c?和?mmdecode.c变汪。對于這樣小的程序侠坎,也許看起來有些過頭了。但是小的程序很難保證一直小下去裙盾,因此哥忒拓展做好計劃是一個“好主意”实胸。

? ? ? ?但是那些?.h?文件呢他嫡?我會在后面解釋一般的術(shù)語,簡單地說庐完,它們被稱為頭文件钢属,同時它們可以包含 C 語言類型定義和 C 預(yù)處理指令。頭文件中不應(yīng)該包含任何函數(shù)门躯。你可以認為頭文件是提供了應(yīng)用程序接口(API)的定義的一種?.c?文件淆党,可以供其它?.c?文件使用。


但是 Makefile 是什么呢讶凉?

? ? ? ?我知道下一個轟動一時的應(yīng)用都是你們這些好孩子們用 “終極代碼粉碎者 3000” 集成開發(fā)環(huán)境來編寫的染乌,而構(gòu)建項目是用 Ctrl-Meta-Shift-Alt-Super-B 等一系列復(fù)雜的按鍵混搭出來的。

? ? ? ?但是如今(也就是今天)懂讯,使用?Makefile?文件可以在構(gòu)建 C 程序時幫助做很多有用的工作荷憋。Makefile?是一個包含如何處理文件的方式的文本文件,程序員可以使用其自動地從源代碼構(gòu)建二進制程序(以及其它東西:滞)

以下面這個小東西為例:

00 # Makefile

01 TARGET= my_sweet_program

02 $(TARGET): main.c

03 ? ?cc -o my_sweet_program main.c

? ? ? ?#?符號后面的文本是注釋勒庄,例如 00 行。

01 行是一個變量賦值瘫里,將?TARGET?變量賦值為字符串?my_sweet_program锅铅。按照慣例,也是我的習(xí)慣减宣,所有?Makefile?變量均使用大寫字母并用下劃線分隔單詞。

02 行包含該步驟recipe要創(chuàng)建的文件名和其依賴的文件玩荠。在本例中漆腌,構(gòu)建目標target是?my_sweet_program,其依賴是?main.c阶冈。

? ? ? ?最后的 03 行使用了一個制表符號(tab)而不是四個空格闷尿。這是將要執(zhí)行創(chuàng)建目標的命令。在本例中女坑,我們使用?C 編譯器C compiler前端?cc?以編譯鏈接為?my_sweet_program填具。

使用?Makefile?是非常簡單的。

$ make

cc -o my_sweet_program main.c

$ ls

Makefile ?main.c ?my_sweet_program

? ? ? ?構(gòu)建我們喵嗚喵嗚編碼器/解碼器的?Makefile?比上面的例子要復(fù)雜匆骗,但其基本結(jié)構(gòu)是相同的劳景。我將在另一篇文章中將其分解為 Barney 風(fēng)格。


形式伴隨著功能

? ? ? ?我的想法是程序從一個文件中讀取碉就、轉(zhuǎn)換它盟广,并將轉(zhuǎn)換后的結(jié)果存儲到另一個文件中。以下是我想象使用程序命令行交互時的情況:

$ meow < clear.txt > clear.meow

$ unmeow < clear.meow > meow.tx

$ diff clear.txt meow.tx

$

? ? ? ?我們需要編寫代碼以進行命令行解析和處理輸入/輸出流瓮钥。我們需要一個函數(shù)對流進行編碼并將結(jié)果寫到另一個流中筋量。最后烹吵,我們需要一個函數(shù)對流進行解碼并將結(jié)果寫到另一個流中。等一下桨武,我們在討論如何寫一個程序肋拔,但是在上面的例子中,我調(diào)用了兩個指令:meow?和?unmeow呀酸?我知道你可能會認為這會導(dǎo)致越變越復(fù)雜凉蜂。


次要內(nèi)容:argv[0] 和 ln 指令

? ? ? ?回想一下,C 語言 main 函數(shù)的結(jié)構(gòu)如下:

int main(int argc, char *argv[])

? ? ? ?其中?argc?是命令行參數(shù)的數(shù)量七咧,argv?是字符指針(字符串)的列表跃惫。argv[0]?是包含正在執(zhí)行的程序的文件路徑。在 Unix 系統(tǒng)中許多互補功能的程序(比如:壓縮和解壓縮)看起來像兩個命令艾栋,但事實上爆存,它們是在文件系統(tǒng)中擁有兩個名稱的一個程序。這個技巧是通過使用?ln?命令創(chuàng)建文件系統(tǒng)鏈接來實現(xiàn)兩個名稱的蝗砾。

? ? ? ?在我筆記本電腦中?/usr/bin?的一個例子如下:

$ ls -li /usr/bin/git*

3376 -rwxr-xr-x. 113 root root ? ? 1.5M Aug 30 ?2018 /usr/bin/git

3376 -rwxr-xr-x. 113 root root ? ? 1.5M Aug 30 ?2018 /usr/bin/git-receive-pack

...

? ? ? ?這里?git?和?git-receive-pack?是同一個文件但是擁有不同的名字先较。我們說它們是相同的文件因為它們具有相同的 inode 值(第一列)。inode 是 Unix 文件系統(tǒng)的一個特點悼粮,對它的介紹超越了本文的內(nèi)容范疇闲勺。

? ? ? ?優(yōu)秀或懶惰的程序可以通過 Unix 文件系統(tǒng)的這個特點達到寫更少的代碼但是交付雙倍的程序。首先扣猫,我們編寫一個基于其?argv[0]?的值而作出相應(yīng)改變的程序菜循,然后我們確保為導(dǎo)致該行為的名稱創(chuàng)建鏈接。

? ? ? ?在我們的?Makefile?中申尤,unmeow?鏈接通過以下的方式來創(chuàng)建:

# Makefile

...

$(DECODER): $(ENCODER)

? ? ? ? $(LN) -f $< $@

? ? ? ...

? ? ? ?我傾向于在?Makefile?中將所有內(nèi)容參數(shù)化癌幕,很少使用 “裸” 字符串。我將所有的定義都放置在?Makefile?文件頂部昧穿,以便可以簡單地找到并改變它們勺远。當(dāng)你嘗試將程序移植到新的平臺上時,需要將?cc?改變?yōu)槟硞€?cc?時时鸵,這會很方便胶逢。

? ? ? ?除了兩個內(nèi)置變量?$@?和?$<?之外,該步驟recipe看起來相對簡單饰潜。第一個便是該步驟的目標的快捷方式初坠,在本例中是?$(DECODER)(我能記得這個是因為?@?符號看起來像是一個目標)。第二個囊拜,$<是規(guī)則依賴項某筐,在本例中,它解析為?$(ENCODER)冠跷。

? ? ? ?事情肯定會變得復(fù)雜南誊,但它還在管理之中身诺。


看到這里你是不是又學(xué)到了很多新知識呢~

如果你很想學(xué)編程,小編推薦我的C語言/C++編程學(xué)習(xí)基地【點擊進入】抄囚!

都是學(xué)編程小伙伴們霉赡,帶你入個門還是簡簡單單啦,一起學(xué)習(xí)幔托,一起加油~

還有許多學(xué)習(xí)資料和視頻穴亏,相信你會喜歡的!

涉及:游戲開發(fā)重挑、常用軟件開發(fā)嗓化、編程基礎(chǔ)知識、課程設(shè)計谬哀、黑客等等......

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刺覆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子史煎,更是在濱河造成了極大的恐慌谦屑,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件篇梭,死亡現(xiàn)場離奇詭異氢橙,居然都是意外死亡,警方通過查閱死者的電腦和手機恬偷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門悍手,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人袍患,你說我怎么就攤上這事谓苟。” “怎么了协怒?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卑笨。 經(jīng)常有香客問我孕暇,道長,這世上最難降的妖魔是什么赤兴? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任妖滔,我火速辦了婚禮,結(jié)果婚禮上桶良,老公的妹妹穿的比我還像新娘座舍。我一直安慰自己,他們只是感情好陨帆,可當(dāng)我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布曲秉。 她就那樣靜靜地躺著采蚀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪承二。 梳的紋絲不亂的頭發(fā)上榆鼠,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天,我揣著相機與錄音亥鸠,去河邊找鬼妆够。 笑死,一個胖子當(dāng)著我的面吹牛负蚊,可吹牛的內(nèi)容都是我干的神妹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼家妆,長吁一口氣:“原來是場噩夢啊……” “哼鸵荠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起揩徊,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤腰鬼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后塑荒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體熄赡,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年齿税,在試婚紗的時候發(fā)現(xiàn)自己被綠了彼硫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡凌箕,死狀恐怖拧篮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情牵舱,我是刑警寧澤串绩,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站芜壁,受9級特大地震影響礁凡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜慧妄,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一顷牌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧塞淹,春花似錦窟蓝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽状共。三九已至,卻和暖如春滑臊,著一層夾襖步出監(jiān)牢的瞬間口芍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工雇卷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鬓椭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓关划,卻偏偏與公主長得像小染,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子贮折,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,107評論 2 356