前言
平常練習 C 語言時,我們可能只用單一或少數 C 原始碼檔案,只用簡單的指令或 IDE 按鈕來編譯 C 原始碼。但我們若想用 C 寫應用程式或函式庫,應該要以專案的形式管理 C 原始碼。本文介紹建立 C 專案的方式。
什麼是 C 專案?
C 專案是管理上的概念。如同其他的軟體專案,C 專案有特定的目標。像是一個處理 JSON 格式檔案的函式庫 json-c 就是一個 C 專案。
在電腦中,C 專案有兩項基本要件:
- 將 C 程式碼集中在特定目錄或該目錄的子目錄中
- 使用自動編譯軟體進行自動化編譯
至於其他的項目,像是說明文件、版本控制等,都是輔助性的東西。對於使用 C 專案本身來說,不是必備的。
C 專案的種類
根據專案的產出,可將 C 專案分為應用程式 (application) 和函式庫 (library) 兩大類。應用程式為可執行的電腦程式 (program)。函式庫則是預寫的函式、巨集等,給外部程式使用。
根據使用者介面又可再將應用程式分為命令列工具 (console application)、圖形界面程式 (GUI application)、網頁程式 (web application)、網頁前端程式 (web front-end application)、應用程式伺服器 (application server) 等。
註:C 和 C++ 程式碼可編譯成 WebAssembly 後,在網頁前端執行。
函式庫則可分為靜態函式庫 (static library) 和動態函式庫 (dynamic library 或 shared library) 兩種。兩者使用的方式相異。
靜態函式庫會在編譯時將函式庫的程式碼「貼」到主程式中,所以主程式發佈時不需要攜帶靜態函式庫。但主程式的體積會變大,而且更新時要直接替換主程式的執行檔。
相對來說,動態函式庫會在執行期才和主程式進行連結。主程式發佈時需要另外攜帶動態函式庫。但使用動態函式庫的應用程式的體積較小,而且更新時可以只更新動態函式庫,不用替換主程式。
C 專案不一定僅限單一產出形式。我們可以把專案的核心功能包成函式庫,再自己寫不同界面的應用程式呼叫此函式庫,就可以讓多種形式的應用程式共享相同的核心功能。
C 語言原始碼
C 語言的原始碼分為標頭檔 (header) 和程式碼 (source) 兩部分。標頭檔為 C 程式的宣告,程式碼則是 C 程式的實作。
我們偶爾可以在網路上看到有些開發者分享號稱 header-only 的 C 專案,但卻把實作寫在標頭檔內。雖然這是合法的 (valid),但不建議把實作寫在標頭檔內。因為這樣做有可能會造成重覆引入的問題。
按照 C 語言的慣例,不論在那個系統上,標頭檔的副檔名皆為 .h ,而程式碼的副檔名則為 .c 。C 語言不會像 Java 般刻意限制檔案名稱,所以檔案名稱只要簡明易懂即可。
C 專案的架構
C 語言沒有內定的專案架構,可參考一些現有的 C 專案架構來建立自己的 C 專案。
如果檔案數量很少,直接用扁平式的專案架構也無妨。這時候,所有的標頭檔、程式碼、自動編譯設定檔等,都會放置在專案的根目錄下。
以下用 tree(1)
展示一個假想的扁平專案:
$ cd path/to/project
$ tree
.
├── Makefile
├── list.c
├── list.h
└── test_list.c
如果檔案比較多,就會建議用巢狀式的專案架構。這時候、標頭檔、程式碼、測試程式、範例程式等,會各自放在不同的子目錄。以下是常見的子目錄名稱:
- src :存放原始碼和內部標頭檔
- include :存放公開標頭檔
- dist :存放專案產出
- bin :當專案產出為應用程式時,可用 bin 取代 dist
- test :存放測試程式
- example :存放範例程式
- doc :存放說明文件
我們同樣用 tree(1)
來展示一個假想的巢狀專案:
$ cd path/to/project
$ tree -L 1
.
├── Makefile
├── dist
├── include
├── src
└── test
大型專案則會用子目錄進一步細分 C 原始碼,像是 GNOME 的 glib 和 gtk 都採用這樣的方式來設置專案架構。
C 專案的輔助文件
除了標頭檔和程式碼外,C 專案還會有一些輔助文件。雖然這些文件不是原始碼的一部分,但也對了解專案有所幫助。以下是常見的輔助文件:
- README 或 README.md :讀我檔案
- INSTALL :安裝說明
- LICENSE :專案的授權文件
- HISTORY :專案的版本釋出記錄
- .gitignore :讓 Git 忽略某些形態的目錄或檔案
- .travis.yml :Travis CI 的設定檔
- Makefile :
make(1)
或gmake(1)
預設的設定檔 - configure :Autotools 的命令稿
- CMakeLists.txt :
cmake(1)
預設的設定檔
這些文件會放在專案的根目錄。
README 是專案的介紹文件。目的是讓接觸此專案的程式設計者對專案有初步的認識。由於 README 對於專案有著推廣的作用,應該儘量用簡明的文字來撰寫。
許多專案會刻意使用 README.md 為讀我檔案的名稱,並用 Markdown 格式來書寫讀我檔案。因為 GitHub、GitLab 等程式碼專案管理網站會自動解析該文件,根據該文件生成漂亮的說明文件。
INSTALL 是專案的安裝說明文件。該文件會記載專案的相依性、編譯工具、編譯指令等。理想的 INSTALL 文件應該讓專案使用者可以只閱讀該文件就足以順利編譯和安裝專案程式。
LICENSE 是專案的授權文件。由於 GitHub、GitLab 等網站會解析該文件,並在頁面上自動顯示專案的授權,授權文件最好保持此名稱。
現在的程式碼專案大多會用版本控制系統 (version control system) 管理。Git 是近年來最知名的版本控制軟體。 .gitignore 用來控制不應存入專案內的檔案。C.gitignore 是 GitHub 給 C 專案的忽略清單,若有使用 Git 管理專案,可以參考一下。
CI (continuous integration) 是自動化編譯及測試的一環。簡單地說,就是在雲端自動編譯及測試程式碼專案後,確認程式碼專案可正常運作,CI 服務會以電子郵件等方式通知專案擁有者。常見的 CI 有 Travis CI 等。
C 專案的標頭檔和原始碼只是靜態檔案,要自動化編譯得使用外部工具來管理。常見的工具有 Make、Autotools、CMake 等。我們會在後續文章介紹。
C 專案產生器 (C Project Generator)
C 語言並沒有內定的專案產生器,一些 C 和 C++ IDE 自動建立 C 專案的功能只是輔助,而不是必需的。正規的方法是慢慢手動建目錄、C 原始檔、自動編譯設定檔、輔助文件等。因為 C 沒有制式的專案架構,只要專案架構合理即可。
網路上有一些 C 專案產生器。但這些 C 專案產生器只是把原本手動建置專案的過程用電腦程式自動化而已,真正管理專案的還是 Make、CMake 等自動編譯系統。所以這些 C 專案產生器沒有成為市場主流。
如果不想用 C 專案產生器,另一個替代的方式是寫樣板專案。日後在寫新專案時,就可以省去從頭設置專案的時間。這裡有給 C 應用程式用的樣板專案和給 C 函式庫使用的樣板專案,有需求的讀者可自行取用。
結語
在本文中,我們介紹了 C 專案的概念及建立方式。但限於文章篇幅,沒有介紹 C 專案中最重要的自動編譯系統。我們會在後續的文章介紹自動編譯系統的部分。