前言
本文對 C 語言做概念性的介紹。
為什麼要學 C 語言?
相較於其他語言,C 語言更貼近硬體,抽象程度較低,適合用來理解電腦程式的基本運作方式。相對地,撰寫程式時需要自行處理較多細節。
一般基礎教材或大專院校課程中的 C 語言,多屬入門內容,與實際軟體專案之間仍有明顯落差。此外,主流桌面系統(如 Windows)對 C 的開發體驗並不理想,容易讓人產生「C 語言不實用」的印象。
C 語言是高階語言還是低階語言?
結論先說:C 語言是高階語言。
所謂低階語言,通常指的是組合語言(assembly language)。組合語言直接對應處理器(CPU)的指令集(instruction set),幾乎是一對一地控制硬體。因此,不同處理器之間的組語無法互通,也就不具備跨平台能力。
C 語言則不同。C 程式碼需要經由編譯器轉換為組合語言,再進一步轉為機械碼執行。其語法本身並不直接對應特定處理器的指令集,因此被歸類為高階語言。
有時會有人稱 C 為「中階語言」,但這並非嚴格的分類。C 仍然是高階語言,只是抽象程度較低。許多在其他語言中屬於語言本身的功能,在 C 中通常由函式庫提供,或需要自行實作。
跨平台
C 語言最初是為了撰寫 Unix 系統而設計。當 Unix 以 C 語言重新實作後,系統的可攜性大幅提升:在移植到新硬體時,只需重寫少部分與處理器相關的組合語言,其餘以 C 撰寫的程式碼可以直接重用。
不過,C 的跨平台並不像 Java、Golang 等語言那樣自動化。C 程式需要直接面對各平台的系統 API 差異。除了語法與標準函式庫層級的可攜性之外,實務上仍需由程式設計者主動設計與抽象,才能寫出真正可跨平台的程式。
C 語言的演進
有些人以為 C 語言是老古板,但其實 C 語言仍在持續演化。我們這裡會簡介到西元 2020 年為止的 C 標準演進過程。
K&R C
K&R C 是 C 語言尚未標準化前的非正式標準。該規格記載在 Brian Kernighan 和 Dennis Ritchie 所著的 C 語言經典教材 The C Programming Language 第一版中。由於 C 語言已經標準化了,不需再刻意追隨這個版本的 C 語言。
C89 或 ANSI C
C89 是第一個正式的 C 標準,許多人對 C 語言的印象就是基於這個版本的 C。由於絕大部分的 C 編譯器至少都支援 C89,有些很在意程式碼可攜性的軟體專案會刻意守在這個版本,像是 Lua。
C99
C99 是第一個 C 標準的重大改版,加入許多新的功能。包括但不限於:
- 新增布林數 (boolean)
- 新增複數 (complex number)
- 新增
long long整數 - 新增以
//開頭的單行註解 - 可在
for迴圈內初始化變數 - 可用自動變數決定陣列長度
- 新增一些函式庫
如果沒寫過 C 程式碼,可能會無法理解這些特性。先大略看過,學一陣子 C 語言自然會了解。
大抵上,C99 相容於 C89,但新增一些功能。
C11
C11 是第二個 C 標準的重大改版,加入許多新的功能。包括但不限於:
- 新增型別安全的泛型程式,使用
_Generic保留字 - 支援多執行緒程式
- 加入更多浮點數運算相關的巨集 (macro)
- 支援匿名結構體 (structure) 和匿名聯合 (union)
- 改善對 Unicode 的支援
同樣地,C11 大抵上相容於先前的 C 標準。
現代 C 語言 (modern C) 是指充份利用 C99 和 C11 的特性來撰寫 C 程式碼,不用刻意守在 ANSI C。善用現代 C 語言所帶來的特性,會讓程式碼更簡潔易讀。除非專案需要守在 ANSI C 或是所用的編譯器無法充分支援現代 C 語言的特性,我們應該善用現代 C 語言所帶來的便利性。
C17 或 C18
C18 是一個小改版,沒有引入新的語法特性,僅修復一些先前的問題。
C23
C23 也稱為 C2x,是最新的 C 標準。這個版本引入一些新的特性。但主流編譯器還沒跟上,不用急著去追新。
Embedded C
初學 C 語言時,會預設 C 程式在個人電腦上運行。相對來說,embedded C 是用於嵌入式裝置的 C 標準,和一般 C 語言有些差異。剛學 C 語言時不用刻意學這套標準,等到熟悉 C 語言後再學也不遲。
POSIX
POSIX 不是 C 標準,而是類 Unix 系統的標準,用於 BSD、GNU/Linux 等系統。POSIX 中即定義了一套共通的 C API,所以 C 程式碼在不同類 Unix 系統間是可攜的。由於 GNU/Linux 等類 Unix 系統相當普遍,所以 POSIX 值得關注。
主要的 C 語言編譯器
語言規格只是一份技術文件,實際能否使用,取決於編譯器是否支援。
目前常見的 C 編譯器包括 Visual C++、GCC 與 Clang。各編譯器對標準的支援情況可能隨版本變動,實務上仍應以官方文件為準。
Visual C++
Visual C++ 是隨附於 Visual Studio 的 C/C++ 編譯器,提供完整的開發環境。由於安裝與使用門檻較低,是許多初學者在 Windows 上學習 C 語言時的常見選擇。
GCC
GCC 是 GNU/Linux 等類 Unix 系統常見的編譯器,也有移植到其他平台。GCC 對 C 標準支援完整,並允許透過參數指定不同標準版本。
此外,GCC 提供部分非標準的語言延伸(extensions)。這些特性可提升表達能力,但會降低程式碼的可攜性。
Clang
Clang 是另一套常見的 C/C++ 編譯器,廣泛用於 macOS,也支援多種平台。其設計與 GCC 具有一定程度的相容性。
在學習階段,Clang 通常提供較清楚的錯誤訊息,有助於理解與除錯。
其他 C 語言編譯器
除了通用型編譯器外,也有針對特定用途或硬體設計的工具。
Intel C++ Compiler
Intel 提供的編譯器著重於效能最佳化,特別是在 Intel 處理器上。這類工具多用於效能敏感的應用場景,對初學者而言並非必要。
CUDA
CUDA 是利用 NVIDIA 顯示卡進行平行運算的技術,並在 C 語言上加入延伸語法。由於需依賴特定硬體與非標準特性,通常不作為入門學習工具。
Arduino
Arduino 是常見的嵌入式開發平台,使用接近 C/C++ 的語法撰寫程式。不過其語言環境與標準 C 存在差異,通常在特定應用情境下才會接觸。
和 C 語言相關的程式語言
C 語言簡潔的設計影響了許多後續的程式語言。其中有些語言直接建立在 C 之上,例如 C++ 和 Objective-C,並在此基礎上加入物件系統等較高階的抽象。
C++ 在語言本身中整合了物件導向、泛型等特性,發展成一門規模龐大的語言。雖然語法上與 C 有高度相似性,但兩者已經是不同的語言,也不再存在嚴格的包含關係。
Objective-C 則是在 C 的基礎上加入物件導向機制,主要應用於 Apple 生態系(macOS、iOS)。隨著 Swift 的出現,其重要性已逐漸下降,但在既有系統中仍然被廣泛使用。
本系列文章的記述方式
對於完整的 C 程式碼,會以語法高亮來輔助閱讀:
#include <stdio>
int main (void)
{
printf("Hello World\n");
return 0;
}
同樣地,對於 C 程式碼片段,也會以語法高亮來輔助閱讀:
/* Excerpt */
assert(0 != strcmp("hello", "goodbye"));
Unix (含 macOS、GNU/Linux、FreeBSD 等) 終端機會以 $ 符號來表示指令提示符:
$ cd path/to/project
當使用 root 操作 Unix 終端機時,則會改用 # 來表示指令提示符:
# apt install gcc
Windows 終端機會以 > 來表示指令提示符。為了簡化,不顯示工作目錄:
> cd path\to\project