[C 語言] 程式設計教學:簡介

【分享本文】
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

    前言

    在本章中,我們不急著動手寫程式。而會先對 C 語言做一些概念上的介紹。

    為什麼要學 C 語言?

    除了大專院校會把 C 語言當成教學工具外,C 語言和他的大兄弟 C++ 可說是資訊界最重要的兩個語言。許多重要的軟體專案是以 C 語言寫成:

    許多高階語言的編譯器或直譯器也是以 C 寫成:

    • GCC:GNU 計劃的 C 和 C++ 編譯器
    • Perl
    • Python
    • Ruby
    • PHP:全世界約 78% 的網站使用 PHP 實作,包括 Facebook
    • Lua

    同理,許多高階語言的延伸模組內部以 C 語言或 C++ 實作,再用該語言的 C API 做 binding。

    此外,在嵌入式系統中,由於系統資源受限,通常會用組合語言和/或 C 語言來寫程式,較少用其他的程式語言。

    在所有程式語言中,除了組合語言外,最貼近電腦硬體的語言就是 C 語言。對於學習電腦相關知識來說,學習 C 語言會比其他高階語言更有幫助。

    雖然現在有一些新的系統語言,像是 D 語言或 Rust,C 語言並沒有因這些語言出現就衰退掉。至少現有的大型軟體專案不會因為新語言出現就全面重寫。這些新興系統語言只是實作新專案的選項之一,而非用來替代 C 的方案。

    我們在市面上的基礎教材和大專院校中所學到的 C 語言算是入門級內容,和上述的重量級軟體專案有一大段落差。此外,一般人用的桌面系統 Windows 對 C 的支援不佳。所以會給人 C 語言不實用的錯覺。

    C 語言是高階語言還是低階語言?

    先說答案,C 語言是高階語言。

    真正的低階語言是組合語言 (assembly language)。所謂的組合語言就是直接以處理器的指令集 (instruction set) 來寫程式。由於每種處理器有自己獨特的指令集,故組語無法跨平台。

    原本 C 語言的出現,就是要解決 Unix 系統的跨平台議題。C 語言的語法,無法直接和處理器的指令集做一對一的轉換,而要經過 C 編譯器將 C 程式碼轉換成等效的組語程式碼後再轉為機械碼。所以 C 語言是高階語言。

    有些程式人會認定 C 語言是中階語言。實際上,沒有什麼所謂的中階語言。C 語言和其他高階語言的差別只是 C 語言的抽象化程度比較少。由於抽象化程度較少,所以 C 程式碼在編譯時能夠轉換成體積較小且效能佳的機械碼。

    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 是一個小改版,沒有引入新的語法特性,僅修復一些先前的問題。

    C2x

    C2x 是最新的 C 標準,這個版本將會引入一些新的特性。但這個版本還沒有穩定下來,所以,不用急著去追新。

    Embedded C

    初學 C 語言時,會預設 C 程式在個人電腦上運行。相對來說,embedded C 是用於嵌入式裝置的 C 標準,和一般 C 語言有些差異。剛學 C 語言時不用刻意學這套標準,等到熟悉 C 語言後再學也不遲。

    POSIX

    POSIX 不是 C 標準,而是類 Unix 系統的標準,用於 GNU/Linux 等系統。POSIX 中即定義了一套共通的 C API,所以 C 程式碼在不同類 Unix 系統間是可攜的。由於 GNU/Linux 等類 Unix 系統相當普遍,所以 POSIX 值得關注。

    主要的 C 語言編譯器

    語言規格只是一份技術性文件,規格上所列的特性是否能用,還得看 C 編譯器是否有實作。

    本節來看目前市面上主要的 C 語言編譯器對 C 標準的支援情形。由於這裡的資訊會隨時間變動,建議最好查閱一下各編譯器的文件,不要只依賴本文的資訊。

    Visual C++

    Visual C++ 是隨附在 Visual Studio 內的 C 和 C++ 編譯器。由於 Visual Studio Community 裝好就有完整的 C 編譯器和 IDE (整合性開發環境),是很多人學習 C 語言時所用的工具。

    然而,如果有心要學現代 C 語言,不建議使用 Visual Studio 來學。從微軟官方文件來看,微軟並沒有提到 Visual C++ 對 C 語言支援到什麼程度。從一些討論區的文章側面得知,Visual C++ 僅支援一部分的 C99,不支援 C11。由於我們不知道 Visual C++ 支援到什麼程度,只能邊寫邊試,無形中浪費不少時間。

    但有時候 Visual C++ 反而會強制使用具有邊界檢查的 _s 後綴函式,這些函式是在 C11 才出現的。由此可知,Visual C++ 也不完全遵守 ANSI C 標準。對於注重 C 標準的學習者來說,Visual C++ 不是很好的工具。

    GCC

    GCC 是 GNU/Linux 等類 Unix 系統所用的 C 和 C++ 編譯器,也有移植到 Windows 的版本。GCC 還蠻認真在支援 C 標準上,ANSI C、C99 和 C11 均有支援,而且可藉由參數指定 C 標準。

    此外,GCC 自帶特有的 C extension,用來強化 C 語言的特性。要注意這些特性不是 C 標準的一部分,會使得 C 程式碼可攜性變差。如果已確認專案只會使用 GCC 來編譯,仍可考慮使用這些特性。

    Clang

    Clang 是 MacOS 預設的 C 和 C++ 編譯器,也有移植到 Windows 和類 Unix 系統上。Clang 刻意在參數上和 GCC 相容,但部分特性仍和 GCC 相異,故無法完全取代 GCC。Clang 有支援 ANSI C、C99 和 C11。

    站在學習的觀點上,可以考慮用 Clang 來學習 C 語言。因為 Clang 吐出的錯誤訊息比 GCC 友善,對 C 語言初學者來說比較容易除錯。

    其他 C 語言編譯器

    除了上述通用型 C 編譯器,還有一些針對特定硬體所製作的 C 編譯器。我們在本節中介紹幾個常見的例子。

    Intel C++ Compiler

    顧名思義,Intel C++ Compiler 會對使用 Intel CPU 的電腦做程式碼優化。但對非 Intel CPU 的電腦就吃不到這些福利。根據 Intel 所發佈的調校數據,Intel C++ Compiler 所編譯出來的程式的確會比 GCC 或 Clang 所編譯的快一些。

    Intel C++ Compiler 主要的特長是優化 C 程式碼,對於 C 語言學習者來說,不需要刻意用這套編譯器來練習 C 語言。如果對這套編譯器有興趣,Intel System Studio 可免費或付費使用,而 Intel Parallel Studio XE 則需付費使用。

    CUDA

    CUDA 是使用 NVIDIA 的顯示卡進行平行運算的技術。CUDA C 會在原本的 C 語言之外加入延伸語法,用來撰寫平行運算相關的程式碼。由於 CUDA 運算需搭配 NVIDIA 顯示卡,而且會用到非標準的 C 延伸語法,一開始不用刻意學 CUDA C。

    Arduino

    Arduino 是開放原始碼的單板電腦,程式人可使用 C 或 C++ 撰寫在 Arduino 上執行的程式。但 Arduino 程式是用 C 和 C++ 的子集來撰寫,和標準 C 有差異。除非對嵌入式系統有興趣,不需一開始就用 Arduino 學 C 語言。

    和 C 語言相關的程式語言

    C 語言簡潔的設計,間接影響了許多高階語言的特性。在這些高階語言中,C++ 和 Objective-C 直接和 C 語言相關,可視為 C 語言的超集合。由於 C 語言缺乏內建的物件系統,這兩個語言使用不同的物件系統來改善這項缺失。

    C++

    C++ 早期是以 C++ 程式碼轉 C 程式碼的轉譯器來實作,後來則變成實質的編譯器。在 C++ 演進的過程中,加入了許多特性,使得 C++ 成為兼具中階和高階特性的程式語言,相當龐大且複雜。

    現在的 C++ 程式包含以下四種面向:

    • 命令式程式設計,這部分和 C 語言重疊
    • 物件導向程式設計
    • 泛型程式設計
    • 函數式程式設計

    近年來,C++ 飛快地發展,和 C 語言差異越來越大。而且,C++ 並不是 C 語言的嚴格超集。因此,現在普遍認為,學 C++ 時不需先學 C

    由於 C++ 兼容 C 的特性,在高階語法特性上又比 C 豐富得多,許多程式人在學完 C 之後,轉而學習和使用 C++。除了資源受限等不適合用 C++ 的情境,基本上 C 程式碼都可以用 C++ 改寫。

    Objective-C

    Objective-C 早期使用 C 前置處理器來撰寫,後來演化成實質的編譯器。在 Swift 問世前,Objective-C 是 Mac 家族系統主要的程式語言。目前為了支援現有的軟體,Mac 家族系統仍保留 Objecitve-C API。但由蘋果公司這幾年的發展趨勢來看,蘋果公司幾乎不會再為 Objective-C 開發新特性。

    Objective-C 的語法是 C 語言的嚴格超集,但 Objective-C 所依賴的物件庫,目前只在 Mac 和 iOS 上的 Cocoa 有最完整的實作。在非 Mac 家族系統上,只能用 GNUstep 這套 Cocoa 的仿作品來替代。但 Cocoa 和 GNUstep 的 API 目前不完全相容。

    此外,目前支援 Objective-C 的編譯器 GCC 和 Clang 兩者對 Objective-C 的特性不完全相容。有些新的特性只有在 Clang 上實作,但 GCC 沒有跟上來。所以,Objective-C 沒有辦法像 C 或 C++ 般,成為真正的通用型程式語言。在實務上,可將 Objective-C 視為 Mac 家族系統專用語言。

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤新文章】
    Facebook Twitter Plurk
    標籤: C 語言