C語言探索之旅 | 第二部分第一課:模塊化編程

做者 謝恩銘,公衆號「程序員聯盟」(微信號:coderhub)。
轉載請註明出處。
原文: https://www.jianshu.com/p/207...

《C語言探索之旅》全系列程序員

內容簡介


  1. 前言
  2. 函數原型
  3. 頭文件
  4. 分開編譯
  5. 變量和函數的做用範圍
  6. 總結
  7. 第二部分第二課預告

1. 前言


上一課是 C語言探索之旅 | 第一部分練習題編程

話說上一課是第一部分最後一課,如今開始第二部分的探索之旅!小程序

在這一部分中,咱們會學習 C語言的高級技術。這一部份內容將是一座高峯,會挺難的,可是咱們一塊兒翻越。安全

俗語說得好:「一口是吃不成一個胖子的。」微信

可是一小口一小口,慢慢吃,仍是能吃成胖子的嘛。因此要細水長流,肥油慢積,一路上有你(「油膩」)~編輯器

一旦你跟着咱們的課程一直學到這一部分的結束,你將會掌握 C語言的核心技術,也能夠理解大部分 C語言寫的程序了。模塊化

到目前爲止咱們的程序都只是在一個 main.c 文件裏搗騰,由於咱們的程序還很短小,這也足夠了。函數

但若是以後你的程序有了十多個函數,甚至上百個函數,那麼你就會感到所有放在一個 main.c 文件裏是多麼擁擠和混亂。學習

正由於如此,計算機科學家纔想出了模塊化編程。原則很簡單:與其把全部源代碼都放在一個 main.c 當中,咱們將把它們合理地分割,放到不一樣的文件裏面。測試

2. 函數原型


到目前爲止,寫自定義函數的時候,咱們都要求你們暫時把函數寫在 main 函數的前面。

這是爲何呢?

由於這裏的順序是一個重要的問題。若是你將本身定義的函數放置在 main 函數以前,電腦會讀到它,就會「知道」這個函數。當你在 main 函數中調用這個函數時,電腦已經知道這個函數,也知道到哪裏去執行它。

可是假如你把這個函數寫在 main 函數後面,那你在 main 函數裏調用這個函數的時候,電腦還不「認識」它呢。你能夠本身寫個程序測試一下。是的,很奇怪對吧?這絕對有點任性的。

那你會說:「C語言豈不是設計得很差麼?」

我「徹底」贊成(可別讓 C語言之父 Dennis Ritchie 聽到了...)。可是請相信,這樣設計應該也是有理由的。計算機先驅們早就想到了,也提出瞭解決之道。

下面咱們就來學一個新的知識點,藉着這個技術,你能夠把你的自定義函數放在程序的任意位置。

用來聲明一個函數的「函數原型」


咱們會聲明咱們的函數,須要用到一個專門的技術:函數原型,英語是 function prototype。function 表示「函數」,prototype 表示「原型,樣本,模範」。

就比如你對電腦發出一個通知:「看,個人函數的原型在這裏,你給我記住啦!」

咱們來看一下上一課舉的一個函數的例子(計算矩形面積):

double rectangleArea(double length, double width)
{
    return length * width;
}

怎麼來聲明咱們上面這個函數的原型呢?

  1. 複製,粘貼第一行。
  2. 在最後放上一個分號(;)。
  3. 把這一整行放置在 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 函數很熟悉,是常常打交道的「哥們」,因此不須要函數原型來「介紹」 main 函數。

還有一點,在寫函數原型的時候,對於圓括號裏的函數參數,名字是不必定要寫的,能夠只寫類型。

由於函數原型只是給電腦作個介紹,因此電腦只須要知道輸入的參數是什麼類型就夠了,不須要知道名字。因此咱們以上的函數原型也能夠簡寫以下:

double rectangleArea(double, double);

看到了嗎,咱們能夠省略 length 和 width 這兩個變量名,只保留 double(雙精度浮點型)這個類型名字。

千萬不要忘了函數原型末尾的分號,由於這是編譯器區分函數原型和函數定義開頭的重要指標。若是沒有分號,編譯時會出現比較難理解的錯誤提示。

3. 頭文件


頭文件在英語中是 header file。header 表示「數據頭,頁眉」,file 表示「文件」。

每次看到這個術語,我都想到已經結婚的「咱們的青春」:周杰倫 的《頭文字D》。

到目前爲止,咱們的程序只有一個 .c 文件(被稱爲「源文件」,在英語中是 source file。source 表示「源,源頭,水源」),好比咱們以前把這個 .c 文件命名爲 main.c。固然名字是無所謂的,起名爲hello.c,haha.c 都行。

一個項目多個文件


在實際編寫程序的時候,你的項目通常確定不會把代碼都寫在一個 main.c 文件中。固然,也不是不能夠。

可是,試想一下,若是你把全部代碼都塞到這一個 main.c 文件中,那若是代碼量達到 10000 行甚至更多,你要在裏面找一個東西就太難了。也正是由於這樣,一般咱們每個項目都會建立多個文件。

那以上說到的項目是指什麼呢?

以前咱們用 CodeBlocks 這個 IDE 建立第一個 C語言項目的時候,其實已經接觸過了。

一個項目(英語是 project),簡單來講是指你的程序的全部源代碼(還有一些其餘的文件),項目裏面的文件有多種類型。

目前咱們的項目還只有一個源文件:main.c 。

看一下你的 IDE,通常來講項目是列在左邊。

如上圖,你能夠看到,這個項目(在 Projects 一欄裏)只有一個文件:main.c 。

如今咱們再來展現一個包含好多個文件的項目:

上圖中,咱們能夠看到在這個項目裏有好幾個文件。實際中的項目大可能是這樣的。你看到那個 main.c 文件了嗎?一般來講在咱們的程序中,會把 main 函數只定義在 main.c 當中。

固然也不是非要這樣,每一個人都有本身的編程風格。不過但願跟着這個課程學習的讀者,能夠和咱們保持一致的風格,方便理解。

那你又要問了:「爲何建立多個文件呢?我怎麼知道爲項目建立幾個文件合適呢?」

答案是:這是你的選擇。一般來講,咱們把同一主題的函數放在一個文件裏。

.h 文件和 .c 文件


在上圖中,咱們能夠看到有兩種類型的文件:一種是以 .h 結尾的,一種是以 .c 結尾的。

  • .h 文件:header file,表示「頭文件」,這些文件包含了函數的原型。
  • .c 文件:source file,表示「源文件」,包含了函數自己(定義)。

因此,一般來講咱們不常把函數原型放在 .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* screen)
{
    // ...
}

看到了嗎,其實你早就熟悉了,要引入頭文件,只須要用 #include 這個預處理指令。

所以咱們在 game.c 源文件中一共引入了三個頭文件:stdlib.h, stdio.h,game.h。

注意到一個不一樣點了嗎?

在標準庫的頭文件(stdlib.h,stdio.h)和你本身定義的頭文件(game.h)的引入方式是有點區別的:

  • <> 用於引入標準庫的頭文件。對於 IDE,這些頭文件通常位於 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 環境下,那就要到系統文件夾裏去找,這裏不討論了,感興趣的讀者能夠去網上搜索。

在個人狀況,由於安裝的是 CodeBlocks 這個 IDE,因此在 Windows下,個人頭文件們「隱藏」在這兩個路徑下:

C:\Program Files\CodeBlocks\MinGW\include

C:\Program Files\CodeBlocks\MinGW\x86_64-w64-mingw32\include

通常來講,都在一個叫作 include 的文件夾裏。

在裏面,你會找到不少文件,都是 .h 文件,也就是 C語言系統定義的標準頭文件,也就是系統庫的頭文件(對 Windows,macOS,Linux 都是通用的,C語言原本就是可移植的嘛)。

在這衆多的頭文件當中,你能夠找到咱們的老朋友:stdio.h 和 stdlib.h。

你能夠雙擊打開這些文件或者選擇你喜歡的文本編輯器來打開,不過也許你會嚇一跳,由於這些文件裏的內容不少,並且好些是咱們還沒學到的用法,好比除了 #include 之外的其餘的預處理指令。

你能夠看到這些頭文件中充滿了函數原型,好比你能夠在 stdio.h 中找到 printf 函數的原型。

你要問了:「OK,如今我已經知道標準庫的頭文件在哪裏了,那與之對應的標準庫的源文件(.c 文件)在哪裏呢?」

很差意思,你見不到它們啦。由於 .c 文件已經被事先編譯好,轉換成計算機能理解的二進制碼了。

「伊人已去,年華不復,吾將何去何從?」

既然見不到原先的它們了,至少讓我見一下「美圖秀秀」以後的它們吧…

能夠,你在一個叫 lib 的文件夾下面就能夠找到,在個人 Windows 下的路經爲:

C:\Program Files\CodeBlocks\MinGW\lib

C:\Program Files\CodeBlocks\MinGW\x86_64-w64-mingw32\lib

被編譯成二進制碼的 .c 文件,有了一個新的後綴名:.a(在 CodeBlocks 的狀況,它的編譯器是 MinGW。MinGW 簡單來講就是 GCC 編譯器的 Windows 版本)或者 .lib(在 Visual C++ 的狀況),等。這是靜態連接庫的狀況。

你在 Windows 中還能找到 .dll 結尾的動態連接庫;你在 Linux 中能找到 .so 結尾的動態連接庫。暫時咱們不深究靜態連接庫和動態連接庫,有興趣的讀者能夠去網上自行搜索。

這些被編譯以後的文件被叫作庫文件或 Library 文件(library 表示「庫,圖書館,文庫」),不要試着去閱讀這些文件的內容,由於是看不懂的亂碼。

學到這裏可能有點暈,不過繼續看下去就會漸漸明朗起來,下面的內容會有示意圖幫助理解。

小結一下:
在咱們的 .c 源文件中,咱們能夠用 #include 這個預處理指令來引入標準庫的 .h 頭文件或本身定義的頭文件。這樣咱們就能使用標準庫所定義的 printf 這樣的函數,電腦就認識了這些函數(藉着 .h 文件中的函數原型),就能夠檢驗你調用這些函數時有沒有用對,好比函數的參數個數,返回值類型,等。

4. 分開編譯


如今咱們知道了一個項目是由若干文件組成的,那咱們就能夠來了解一下編譯器(compiler)的工做原理。

以前的課裏面展現的編譯示例圖是比較簡化的,下圖是一幅編譯原理的略微詳細的圖,但願你們用心理解並記住:

上圖將編譯時所發生的事情基本詳細展現了,咱們來仔細分析:

  • 預處理器(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 的定義。

5. 變量和函數的做用範圍


爲告終束這一課,咱們還得學習最後一個知識點:變量和函數的做用範圍(有效範圍)

咱們將學習變量和函數何時是能夠被調用的。

函數的私有變量(局部變量)

當你在一個函數裏定義了一個變量以後,這個變量會在函數結尾時從內存中被刪除。

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 函數中就不能調用,編譯會出錯。

記住:在函數裏定義的變量只能在函數內部使用,咱們稱之爲 局部變量,英語是 local variable。local 表示「局部的,本地的」,variable 表示「變量」。

全局變量(請避免使用)


全局變量的英語是 global variable。global 表示「全局的,整體的」。

能被全部文件使用的全局變量

咱們能夠定義能被項目的全部文件的全部函數調用的變量。咱們會展現怎麼作,是爲了說明這方法存在,可是通常來講,要避免使用能被全部文件使用的全局變量。

可能這樣作一開始會讓你的代碼簡單一些,可是不久你就會爲之煩惱了。

爲了建立能被全部函數調用的全局變量,咱們需要在函數以外定義。一般咱們把這樣的變量放在程序的開頭,#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)
{
    // 指令
}

如今,你的函數就只能被同一個文件中的其餘函數調用了,項目中的其餘文件中的函數就只「可遠觀而不可褻玩焉」…

小結一下變量的全部可能的做用範圍


  1. 在函數體內定義的變量,若是前面沒加 static 關鍵字,則是局部變量,在函數結束時被刪除,只能在本函數內被使用。
  2. 在函數體內定義,可是前面加了 static 關鍵字,則爲靜態變量,在函數結束時不被刪除,其值也會保留。
  3. 在函數外面定義的變量被稱爲全局變量,若是前面沒有static關鍵字,則其做用範圍是整個項目的全部文件,就是說它能夠被項目的全部文件的函數調用。
  4. 函數外面定義的變量,若是前面加了 static 關鍵字,那就只能被本文件的全部函數調用,而不能被項目其餘的文件的函數調用。

小結一下函數的全部可能的做用範圍


  1. 一個函數在默認狀況下是能夠被項目的全部文件的函數調用的。
  2. 若是咱們想要一個函數只能被本文件的函數所調用,只須要在函數前加上 static 關鍵字。

6. 總結


  1. 一個程序包含一個或多個 .c 文件(通常稱爲源文件,source file。固然,咱們通常也把全部的高級語言代碼叫作源代碼)。一般來講,每一個 .c 文件都有一個和它同名但不一樣擴展名的 .h 文件。.c 文件裏面包含了函數的實際定義,而 .h 文件裏包含函數的原型聲明。
  2. .h 文件的內容被一個叫作預處理器(preprocessor)的程序引入到 .c 文件的開頭。
  3. .c 文件被一個叫作編譯器(compiler)的程序轉換成 .o 的二進制目標文件。
  4. .o 文件又被一個叫作連接器(linker)的程序連接成一個最終的可執行文件(在 Windows 操做系統裏可執行程序的擴展名是 .exe,由於 exe 是英語 executable 的前三個字母,表示「可執行的」。在 Linux 系統裏,可執行程序有很多擴展名(.elf,等),也能夠沒有擴展名)。
  5. 變量和函數都有「有效範圍」,某些時候是訪問不到的。

7. 第二部分第二課預告


今天的課就到這裏,一塊兒加油吧!

下一課:C語言探索之旅 | 第二部分第二課:進擊的指針,C語言的王牌!


我是 謝恩銘,公衆號「程序員聯盟」(微信號:coderhub)運營者,慕課網精英講師 Oscar 老師,終生學習者。 熱愛生活,喜歡游泳,略懂烹飪。 人生格言:「向着標杆直跑」
相關文章
相關標籤/搜索