-- 簡書作者 謝恩銘 轉載請註明出處
第二部分第一課:模塊化編程
上一課是C語言探索之旅 | 第一部分第十課:練習題+習作,至此,我們【C語言探索之旅】的第一部分結束了。
大家肯定注意到了,今天的封面圖片我用了老爺子 Dennis Ritchie(丹尼斯.裏奇。C語言之父)的壁紙,略表敬仰和懷念之情。
還有一個原因:因爲這一部分要開始難起來了,開始學習C語言的高級技術。
圖片上的The Legend,是英語“傳奇”的意思。Dennis Ritchie是2011年去世的。
同年,喬布斯喬幫主也離開了我們。
不過不要忘了這個“喬布斯站在其肩頭”的偉大的人,Dennis Ritchie:C語言之父(近代很多編程語言的靈感都來自C語言,例如C++,Java,Objective-C)。
Dennis Ritchie還算是Unix操作系統之父(與Ken Thompson一起,Unix是近代不少操作系統的先驅,其中Linux系統吸取了Unix系統的特點,蘋果的Mac OS系統底層基於改動過的Unix,Android系統底層基於改動過的Linux系統)。
Dennis Ritchie在發明了C語言後,就用C語言重寫了原本用彙編語言編寫的Unix系統。
而蘋果的MAC OS和iOS系統使用了諸如C語言,Objective-C,C++等來編寫。
以後應該拍一系列早期計算機黑客的傳奇的電影,特別是丹尼斯.裏奇,肯定得拍一部紀錄片。如果沒有Dennis Ritchie,我們今天很多技術都沒法實現。
網上有一篇圖文介紹,叫《Unix英烈傳:圖文細數十五位計算先驅》
http://www.linuxidc.com/Linux/2013-08/89395p2.htm
還有最後一個原因:老爺子長得帥啊,給我們程序員長臉啊。
《灌籃高手》裏櫻木花道稱呼安西教練爲老爺子,願我們的Dennis Ritchie教練也“安息”。
好了,話休絮煩,我們迴歸正題。
第二部分開始
話說上一課是第一部分最後一課,現在開始第二部分的探索之旅!
在這一部分中,我們會學習C語言的高級技術。這一部分內容將是一座高峯,會挺難的。但是我們一起翻越。
俗語說得好:一口是吃不成一個胖子的。
但是小口小口,慢慢吃,還是能吃成胖子的嘛。所以要細水長流,肥油慢積。一路上有你(油膩)~
一旦你跟着我們的課程一直到這一部分的結束,你將會掌握C語言的核心技術,也可以理解大部分C語言寫的程序了。
之後在第三部分,我們就會一起來學習C語言圖形編程,構建C語言的2D遊戲等等。
到目前爲止我們的程序都只是在一個main.c文件裏搗騰,因爲我們的程序還很短小,這也足夠。
但如果之後你的程序有了十多個函數,甚至上百個函數,那麼你就會感到全部放在main.c一個文件裏是多麼擁擠和混亂。
正因爲如此,計算機科學家纔想出了模塊化編程。原則很簡單:與其把所有源代碼都放在一個main.c當中,我們將把它們合理地分割,放到不同的文件裏面。
函數原型
到目前爲止,寫自定義函數的時候,我們都要求大家暫時把函數寫在main函數的前面。
這是爲什麼呢?(唉呀媽呀,想到了蔡明和郭達那個小品...)
因爲這裏的順序是一個重要的問題。如果你將自己定義的函數放置在main函數之前,電腦會讀到它,會知道這個函數,當你在main函數中調用這個函數時,電腦知道這個函數,也知道到哪裏去執行它。
但是假如你把這個函數寫在main函數後面,那你在main函數裏調用這個函數的時候,電腦就不認識它了,你可以自己寫個程序測試一下。是的,很奇怪吧?這絕對有點任性的。
那你會說:“C語言豈不是設計得不好麼?”
我完全同意(別讓上面的老爺子聽到了...)。但是請相信,這樣設計應該也是有理由的,計算機先驅們早就想到了,也提出瞭解決之道。
下面我們就來學一個新的知識點,藉着這個,你可以把你的自定義函數放在程序的任意位置。不必操心總是好事。
用來聲明一個函數的“函數原型”
我們會聲明我們的函數,用我們所說的術語:函數原型(prototype)。
就好比你對電腦發出一個通知:“看,我的函數的原型在這裏,你給我記住啦”。
我們來看一下我們上一課舉的一個函數的例子(計算矩形面積):
double rectangleArea(double length, double width)
{
return length * width;
}
怎麼來聲明我們上面這個函數的原型呢?
-
複製,黏貼第一行
-
在最後放上一個分號;
-
把這一整行放置在main函數前面
簡單嗎?現在你就可以把你的函數的定義放在main函數後面啦,電腦也會認識它,因爲你在main函數前面已經聲明過這個函數了。
你的程序會變成這樣:
#include <stdio.h>
#include <stdlib.h>
// 下面這一行是rectangleArea函數的函數原型
double rectangleArea(double length, double width);
int main(int argc, char *argv[])
{
printf("長爲10,寬爲5的矩形面積 = %f\n", rectangleArea(10, 5));
printf("長爲3.5,寬爲2.5的矩形面積 = %f\n", rectangleArea(3.5, 2.5));
printf("長爲9.7,寬爲4.2的矩形面積 = %f\n", rectangleArea(9.7, 4.2));
return 0;
}
// 現在我們的rectangleArea函數就可以放置在程序的任意位置啦
double rectangleArea(double length, double width)
{
return length * width;
}
與原先的程序相比有什麼改變呢,其實就是在程序的開頭加了函數的原型而已(記得不要忘了那個分號)。
函數的原型,其實是給電腦的一個提示或指示。比如上面的程序中,函數原型
double rectangleArea(double length, double width);
就是對電腦說:“老兄,存在一個函數,它的輸入是哪幾個參數,輸出是什麼類型”,這樣就能讓電腦更好地管理。
多虧了這一行代碼,現在你的rectangleArea函數可以置於程序的任何位置啦。
記得:最好養成習慣,對於C語言程序,總是定義了函數,再寫一下函數的原型。
那麼不寫函數原型行不行呢?
也行。只要你把每個函數的定義都放在main函數之前,但是你的程序慢慢會越來越大,等你有幾十或者幾百個函數的時候,你還顧得過來麼?
所以養成好習慣,不吃虧的。
你也許注意到了,main函數沒有函數原型。因爲不需要,main函數是每個C程序必須的入口函數。(人家“有權”,跟編譯器關係好,編譯器對main函數很熟悉,是經常打交道的哥們,所以不需要函數原型來“介紹”main函數)。
還有一點,在寫函數原型的時候,對於圓括號裏的函數參數,名字是不一定要寫的,可以只寫類型。
因爲函數原型只是給電腦做個介紹,所以電腦只需要知道輸入的參數是什麼類型就夠了,不需要知道名字。所以我們以上的函數原型也可以簡寫如下:
double rectangleArea(double, double);
看到了嗎,我們可以省略length和width這兩個變量名,只保留double(雙精度浮點型)這個類型名字。
千萬不要忘了函數原型末尾的分號,因爲這是編譯器區分函數原型和函數定義開頭的重要指標。如果沒有分號,編譯時會出現比較難理解的錯誤提示。
頭文件
每次看到這個術語,我都想到已經結婚的“我們的青春”:周杰倫 的《頭文字D》。
到目前爲止,我們的程序只有一個.c文件(稱之爲“源文件”),比如我們之前把這個.c文件命名爲main.c,當然名字是無所謂的,起名爲hello.c,hehe.c都行。
一個項目多個文件
在實際編寫程序的時候,你的項目一般肯定不會把代碼都寫在一個main.c文件中。當然,可行是可行的。
但是,試想一下,如果你把所有代碼都塞到這一個main.c文件中,那代碼量可能達到10000多行,你要在裏面找一個東西太難了。也正是因爲這樣,通常我們每一個項目都會創建多個文件。
那以上說到的項目是指什麼呢?
之前我們用Code::Blocks這個IDE創建第一個C語言項目的時候,其實有接觸過。但是我們會再解釋一下。
一個項目(英語是 project),簡單來說是指你的程序的所有源代碼(還有一些其他的文件),項目裏面的文件有多種類型。
目前我們的項目還只有一個源文件:main.c
看一下你的IDE,一般來說項目是列在左邊。
如上圖,你可以看到,這個項目(在Projects一欄裏)只有一個文件:main.c
現在我們再來展示一個包含好多個文件的項目:
上圖中,我們可以看到在這個項目裏有好幾個文件。實際中的項目大多是這樣的,你看到那個main.c文件了嗎?通常來說在我們的程序中,會把main函數只定義在main.c當中。
當然不是一定非要這樣,每個人都有自己的編程風格。不過希望跟着這個課程學習的讀者,可以和我們保持一致的風格,方便理解。
那你又要問了:“爲什麼創建多個文件呢?我怎麼知道爲項目創建幾個文件合適呢?”
答案是:這是你的選擇。通常來說,我們把同一主題的函數放在一個文件裏。
.h文件和.c文件
在上圖中,我們可以看到有兩種類型的文件:一種是以.h結尾的,一種是以.c結尾的。
- .h文件:稱爲“頭文件”,這些文件包含了函數的原型
- .c文件:稱爲“源文件”,包含了函數本身(定義)
所以,通常來說我們不常把函數原型放在.c文件中,而是放在.h文件中,除非你的程序很小。
對每個.c文件,都有同名的.h文件。上面的項目那個圖中,你可以看到.h和.c文件一一對應。
- files.h和files.c
- editor.h和editor.c
- game.h和game.c
但我們的電腦怎麼知道函數原型是在.c文件之外的另一種文件裏呢?
需要用到我們之前介紹過的預處理指令 #include來將其引入到.c文件中。
請做好準備,下面將有一波密集的知識點“來襲”。
怎麼引入一個頭文件呢?其實你已經知道怎麼做了,之前的課程我們已經寫過了。
比如我們來看我們上面的game.c文件的開頭
#include <stdlib.h>
#include <stdio.h>
#include "game.h"
void player(SDL_Surface* ecran)
{
// ...
}
看到了嗎,其實你早就熟悉了,要引入頭文件,只需要用#include這個預處理指令。
因此我們在game.c源文件中一共引入了三個頭文件: stdlib.h, stdio.h,game.h
注意到一個不同點了嗎?
在標準庫的頭文件(stdlib.h, stdio.h)和你自己定義的頭文件(game.h)的引入方式是有點區別的:
- <> 用於引入標準庫的頭文件,在IDE中一般位於安裝目錄的include文件夾中,在linux中則一般位於系統的include文件夾裏
- "" 用於引入自定義的頭文件,位於你自己的項目的目錄中
我們再來看一下我們的對應的game.h這個頭文件的內容:
看到了嗎,.h文件中存放的是函數原型。
你已經對一個項目有大致概念了。
那你又會問了:“爲什麼要這樣安排呢?把函數原型放在.h頭文件中,在.c源文件中用#include引入,爲什麼不把函數原型寫在.c文件中呢?”
答案是:方便管理,條理清晰,不容易出錯,省心。
因爲如前所述,你的電腦在調用一個函數前必須先“知道”這個函數,我們需要函數原型來讓使用這個函數的其他函數預先知道。
如果用了.h頭文件的管理方法,在每一個.c文件開頭只要用#include這個指令來引入頭文件的所有內容,那麼頭文件中聲明的所有函數原型都被當前.c文件所知道了,你就不用再操心那些函數的定義順序或者有沒有被其他函數知道
例如我的main.c函數要使用functions.c文件中的函數,那我只要在main.c的開頭寫 #include "functions.h",之後我在main.c函數中就可以調用function.c中定義的函數了。
你可能又要問了:“那我怎麼在項目中加入新的.h和.c文件呢?”
很簡單,在codeblocks裏,鼠標右鍵點擊項目列表的主菜單處,選擇Add Files,或者
在菜單欄上依次單擊 File->New->File...
就可以選擇添加文件的類型了。
引入標準庫
你腦海裏肯定出現一個問題:
如果我們用#include來引入stdio.h和stdlib.h這樣的標準庫的頭文件,而這些文件又不是我自己寫的,那麼它們肯定存在於電腦裏的某個地方,我們可以去找到,對吧?
是的,完全正確!
如果你使用的是IDE(集成開發環境),那麼它們一般就在你的IDE的安裝目錄裏。
如果是在純Linux環境下,那就要到系統文件夾裏去找,這裏不討論了,以後開了Linux課會講到,感興趣的讀者可以去網上搜索。
在我的情況,因爲安裝的是codeblocks這個IDE,所以在windows下,我的頭文件們“隱藏”在這個路徑下:
E:\Program Files\CodeBlocks\MinGW\include
一般來說,都在一個叫做include的文件夾裏。
在裏面,你會找到很多文件,都是.h文件,也就是C語言系統定義的標準頭文件,也就是系統庫的頭文件(對windows,mac,linux都是通用的,C語言本來就是可移植的嘛)。
在這衆多的頭文件當中,你可以找到我們的老朋友:stdio.h和stdlib.h。
你可以雙擊打開這些文件或者選擇你喜歡的文本編輯器來打開,不過也許你會嚇一跳,因爲這些文件裏的內容很多,而且好些是我們還沒學到的用法,比如除了#include以外的其他的預處理指令。
你可以看到這些頭文件中充滿了函數原型,比如你可以在stdio.h中找到printf函數的原型。
你要問了:“ok,現在我已經知道標準庫的頭文件在哪裏了,那與之對應的標準庫的源文件(.c文件)在哪裏呢?”
不好意思,你見不到它們啦。因爲.c文件已經被事先編譯好,轉換成計算機能理解的二進制碼了。
“伊人已去,年華不復,吾將何去何從?”
既然見不到原先的它們了,至少讓我見一下“美圖秀秀”之後的它們吧…
可以啊,你在一個叫lib的文件夾下面就可以找到,在我的Windows下的路經爲:
E:\Program Files\CodeBlocks\MinGW\lib
被編譯成二進制碼的.c文件,有了一個新的後綴名:.a(在codeblocks的情況,因爲編譯器是mingw)或者.lib(在visual c++的情況,因爲編譯器是Visual),當然以後我們還會學到在linux下還有.so這個後綴名,等。暫時不深究。
這些被編譯之後的文件被叫做庫文件或library文件(library是英語“庫”的意思),不要試着去閱讀這些文件的內容,完全不是人看得懂的亂碼…
學到這裏可能有點暈,不過繼續看下去就會漸漸明朗起來,下面的章節會有示意圖幫助理解。
小結一下:
在我們的.c源文件中,我們可以用#include這個預處理指令來引入標準庫的.h頭文件或自己定義的頭文件,這樣我們就能使用標準庫所定義的printf這樣的函數,這樣電腦就認識了這些函數(藉着.h文件中的函數原型),就可以檢驗你調用這些函數時有沒有用對,比如函數的參數個數,返回值類型等。
分開編譯
現在我們知道了一個項目是由若干文件組成的,那我們就可以來了解一下編譯器的工作原理。
之前的課裏面展示的編譯示例圖是比較簡化的,下圖是一幅編譯原理的略微詳細的圖,希望大家用心理解並記住:
上圖將編譯時所發生的事情基本詳細展示了,我們來仔細分析:
- 預處理器(preprocessor):顧名思義,預處理器爲編譯做一些預備工作,所以預處理器是在編譯之前啓動的。它的任務是執行特殊的指令,這些指令是通過預處理命令給出的,預處理命令以#開頭,很容易辨認。
預處理指令有好多種,目前我們學過的只有#include,它使我們可以在一個文件中引入另一個文件的內容。#include這個預處理指令也是最常用的。
預處理器會把#include所在的那一句話替換爲它所引入的頭文件的內容,比如
#include <stdio.h>
預處理器在執行時會把上面這句指令替換爲stdio.h文件的內容。所以到了編譯的時候,你的.c文件的內容會變多,包含了所有引入的頭文件的內容,顯得比較臃腫。
- 編譯(compilation):這是核心的步驟,以前的課我們說過,正是編譯把我們人類寫的代碼轉換成計算機能理解的二進制碼(0和1組成)。編譯器編譯一個個.c文件。對於codeblocks這樣的IDE來說,就是你放在項目列表中的所有.c文件;如果你是用gcc來編譯,那麼你要指定編譯哪幾個.c文件。
編譯器會把.c文件先轉換成.o文件(有的編譯器會生成.obj文件),.o文件一般叫做“目標文件”(o就是英語 object [目標] 的首字母),是臨時的二進制文件,會被用於之後生成最終的可執行二進制文件。
.o文件一般會在編譯完成後被刪除(根據你的IDE的設置)。從某種程度上來說.o文件雖然是臨時中間文件,好像沒什麼大用,但保留着不刪除也是有好處:假如項目有10個.c文件,編譯後生成了10個.o文件。
之後你只修改了其中的一個.c文件,如果重新編譯,那麼編譯器不會爲其他9個.c文件重新生成.o文件了,只會重新生成你更改的那個。節省資源。
- 鏈接器(linker):顧名思義,鏈接器的作用是鏈接,鏈接什麼呢?就是編譯器生成的.o文件,鏈接器把所有.o文件鏈接器來,“製作成”一個“大塊頭”:最終的可執行文件(windows下是.exe文件,linux下有不少種形式)。
現在你知道從代碼到生成一個可執行程序的內部原理了吧,下面我們要展示給大家的這張圖,很重要,希望大家理解並記住。
大部分的錯誤都會在編譯階段被顯示,但也有一些是在鏈接的時候顯示,有可能是少了.o文件之類。
之前那幅圖其實還不夠完整,你可能想到了:我們用.h文件引入了標準庫的頭文件的內容(裏面主要是函數原型),函數的具體實現的代碼我們還沒引入呢,怎麼辦呢?
對了,就是之前提到過的.a或.lib這樣的庫文件(由標準庫的.c源文件編譯而成)。
所以我們的鏈接器(linker)的活還沒完呢,它還需要負責鏈接標準庫文件,把你自己的.c文件編譯生成的.o目標文件和標準庫文件整合在一起,然後鏈接成最終的可執行文件。
如下圖所示:
這下我們的示意圖終於完整了。
這樣我們纔有了一個完整的可執行文件,裏面有它需要的所有指令的定義,比如printf的定義
之後在第三部分我們會用到系統的圖形庫,也是在.a庫文件中定義的,包含了一系列指令的定義,比如告訴電腦怎麼創建一個窗口,繪製圖形,等等。好戲在後頭。
變量和函數的作用範圍
爲了結束我們這一課,我們還必須來學習最後一個知識點:
變量和函數的作用範圍(有效範圍)
我們將學習它們什麼時候是可以被調用的。
函數的私有變量(局部變量)
當你在一個函數裏定義了一個變量之後,這個變量會在函數結尾時從內存中被刪除。
int multipleTwo(int number)
{
int result = 0; // 變量result在內存中被創建
result = 2 * number;
return result;
} // 函數結束,變量result從內存中被刪除
在一個函數裏定義的變量,只在函數運行期間存在。
這意味着什麼呢?意味着你不能從另一個函數中調用它。
#include <stdio.h>
int multipleTwo(int number);
int main(int argc, char *argv[])
{
printf("15的兩倍是 %d\n", multipleTwo(15));
printf("15的兩倍是 %d", result); // 錯誤!
return 0;
}
int multipleTwo(int number)
{
int result = 0;
result = 2 * number;
return result;
}
可以看到,在main函數中,我們試着調用result這個變量,但是因爲這個變量是在multipleTwo函數中定義的,在main函數中就不能調用,會出錯。
記住:在函數裏定義的變量只能在函數內部使用,我們稱之爲局部變量。
全局變量:避免使用
能被所有文件使用的全局變量
我們可以定義能被項目的所有文件的所有函數調用的變量。我們會展示給大家怎麼做,爲了告知大家這方法存在,但是一般來說,要避免使用能被所有文件使用的全局變量。
可能這樣做一開始會讓你的代碼簡單一些,但是不久你就會爲之煩惱了。
爲了創建能被所有函數調用的全局變量,我們須要在函數之外定義。通常我們把這樣的變量放在程序的開頭,#include預處理指令的後面。
#include <stdio.h>
int result = 0; // 定義全局變量result
void multipleTwo(int number); // 函數原型
int main(int argc, char *argv[])
{
multipleTwo(15); // 調用multipleTwo函數,使全局變量result的值變爲原來的兩倍
printf("15的兩倍是 %d\n", result); // 我們可以調用變量result
return 0;
}
void multipleTwo(int number)
{
result = 2 * number;
}
上面的程序中,我們的函數multipleTwo不再有返回值了,而是用於將result這個全局變量的值變成2倍。之後main函數可以再使用result這個變量。
由於這裏的result變量是一個完全開放的全局變量,所以它可以被項目的所有文件調用,也就能被所有文件的任何函數調用。
注:這種類型的變量是很不推薦使用的,因爲不安全。一般用函數裏的return語句來返回一個變量的值。
只能在一個文件裏被訪問的全局變量
剛纔我們學習的完全開放的全局變量可以被項目的所有文件訪問。我們也可以使一個全局變量只能被它所在的那個文件調用。
就是說它可以被自己所在的那個文件的所有函數調用,但不能被項目的其他文件的函數調用。
怎麼做呢?
只需要在變量前面加上 static 這個關鍵字。如下所示:
static int result = 0;
static在英語裏是“靜止的,不變的”之意。
函數的static(靜態)變量
注意:
如果你在聲明一個函數內部的變量時,在前面加上static這個關鍵字,它的涵義和上面我們演示的全局變量是不同的。
函數內部的變量如果加了static,那麼在函數結束後,這個變量也不會銷燬,它的值會保持。下一次我們再調用這個函數時,此變量會延用上一次的值。
例如:
int multipleTwo(int number)
{
static int result = 0; // 靜態變量result在函數第一次被調用時創建
result = 2 * number;
return result;
} // 變量result在函數結束時不會被銷燬
這到底意味着什麼呢?
就是說:result這個變量的值,在下次我們調用這個函數時,會延用上一次結束調用時的值。
有點暈是嗎?不要緊。來看一個小程序,以便加深理解:
#include <stdio.h>
int increment();
int main(int argc, char *argv[])
{
printf("%d\n", increment());
printf("%d\n", increment());
printf("%d\n", increment());
printf("%d\n", increment());
return 0;
}
int increment()
{
static int number = 0;
number++;
return number;
}
上述程序中,在我們第一次調用increment函數時,number變量被創建,初始值爲0,然後對其做自增操作(++運算符),所以number的值變爲1。
函數結束後,number變量並沒有從內存中被刪除,而是保存着1這個值。
之後,當我們第二次調用increment函數時,變量number的聲明語句(static int number = 0;)會被跳過不執行(因爲變量number還在內存裏呢。你想一個皇帝還沒駕崩,太子怎麼能繼位呢)。
我們繼續使用上一次創建的number變量,這時候變量的值沿用第一次increment函數調用結束後的值:1,再對它做++操作(自加1),number的值就變爲2了。
依此類推,第三次調用increment函數後number的值爲3,第四次number的值爲4。
所以運行程序,輸出如下:
1
2
3
4
一個文件中的局部函數(本地函數或靜態函數)
我們用函數的作用域來結束我們關於變量和函數的作用域的學習。
正常來說,當你在一個.c源文件中創建了一個函數,那它就是全局的,可以被項目中所有其他.c文件調用。
但是有時我們需要創建只能被本文件調用的函數,怎麼做呢?
聰明如你肯定想到了:對了,就是使用static關鍵字,與變量類似。
把它放在函數前面。如下:
static int multipleTwo(int number)
{
// 指令
}
現在,你的函數就只能被同一個文件中的其他函數調用了,項目中的其他文件中的函數就只可遠觀而不可褻玩焉…
小結一下變量的所有可能的作用範圍
-
在函數體內定義的變量,如果前面沒加static關鍵字,則是局部變量,在函數結束時被刪除,只能在本函數內被使用。
-
在函數體內定義,但是前面加了static關鍵字,則爲靜態變量,在函數結束時不被刪除,其值也會保留。
-
在函數外面定義的變量被稱爲全局變量,如果前面沒有static關鍵字,則其作用範圍是整個項目的所有文件,就是說它可以被項目的所有文件的函數調用。
-
函數外面定義的變量,如果前面加了static關鍵字,那就只能被本文件的所有函數調用,而不能被項目其他的文件的函數調用。
同樣地,小結一下函數的所有可能的作用範圍
-
一個函數在默認情況下是可以被項目的所有文件的函數調用的。
-
如果我們想要一個函數只能被本文件的函數所調用,只需要在函數前加上static關鍵字。
總結
-
一個程序包含一個或多個.c文件(一般稱爲 源文件,source。當然我們一般也把所有的高級語言代碼叫做源代碼)。通常來說,每個.c文件都有一個和它同名但不同擴展名的.h文件(有點像同父異母的兄弟)。.c文件裏面包含了函數的實際定義,而.h文件裏包含函數的原型聲明。
-
.h文件的內容被一個叫做預處理器(preprocessor)的程序引入到.c文件的開頭。
-
.c文件被一個叫做編譯器(compiler)的程序轉換成.o的二進制目標文件(o是英語object的首字母,表示“對象、目標”)。
-
.o文件又被一個叫做鏈接器(linker)的程序連接成一個最終的.exe可執行文件(exe是英語executable的前三個字母,表示“可執行的”。在windows操作系統裏可執行程序的擴展名是.exe,在linux系統裏,可執行程序有不少擴展名(.elf,等),也可以沒有擴展名)
-
變量和函數都有“有效範圍”,某些時候是訪問不到的。
第二部分第二課預告:
今天的課就到這裏,一起加油咯。
下一次我們學習第二部分第二課。
咳咳,我必須正襟危坐,假裝嚴肅地來宣佈:
來認識一下C語言的精華和王牌:C語言探索之旅 | 第二部分第二課:進擊的指針,C語言的王牌!