前言
我們在本文中說明 C 語言的基本概念。
C 程式碼所用的副檔名
C 程式碼使用兩種副檔名,原始碼 (source) 使用 .c 為副檔名,標頭檔 (header) 使用 .h 為副檔名。原始碼中會放入實作 (implementation),標頭檔則是擺放宣告 (declaration)。
原始碼和標頭檔的檔案名稱沒有特別限制,只要是作業系統中合法的檔名即可。一般建議用簡短、有意義的檔名,並用底線取代空白。
一開始練習的程式規模很小,將所有的程式碼寫在原始碼中無妨。待程式規模變大後,會將原始碼拆分到多個檔案中,這時候就會用到標頭檔。
第一個 C 程式
最小的 C 程式如下:
int main() {}
基本上,這個程式什麼事都沒做。但這個程式太乏味了,我們把這個程式稍微加點東西。以下是加上註解的 Hello World 程式:
/* Include the library for standard input and output. */
#include <stdio.h>
/* Entry to the main program. */
int main(void)
{
/* Print some string to the console. */
printf("Hello World\n");
/* Exit the program successfully. */
return 0;
}
同樣地,這個 Hello World 程式也是簡單到不行,但可由此觀察到 C 程式的架構。
編譯 C 程式的步驟
表面上,編譯 C 程式只是一行指令或一個 IDE 的按鈕就解決的事。但編譯 C 程式實際上分為四個步驟:
- 前處理 (Preprocessing)
- 編譯 (Compilation)
- 組譯 (Assembly)
- 連結 (Linking)
前處理將含巨集 (macro) 的 C 程式碼轉換成沒有巨集的 C 程式碼。嚴格上來說,前處理不算編譯的一部分,只是經由一連串字串代換的動作改寫原始碼而已。我們會在後文介紹 C 巨集。
編譯 (compilation) 是整個編譯 (building) 的前半段。在這個階段,C 編譯器會將 C 原始碼轉換成等效的組合語言原始碼。由此可知,C 仍然是高階語言,只是寫起來不若現代語言那麼方便。
組譯會將組合語言原始碼轉換成機械碼。轉換後的檔案為目的檔 (object files)。目的檔只是編譯的中間產物,無法使用。
最後,透過連結,將目的檔轉為執行檔 (executable)。執行檔即為可使用的電腦程式 (program)。Unix 或類 Unix 系統的執行檔不加副檔名,Windows 系統的執行檔的副檔名為 .exe 。
剛開始練習的程式很簡短,只會用到標準函式庫的函式,所以編譯的步驟較簡單。隨著程式的規模上升,會使用模組化的方式將程式碼分散在多個檔案中,也有可能會用到外部函式庫,這時候編譯的步驟就會變複雜。
大小寫敏感性 (Case Sensitivity)
C 語言的程式碼會區分大小寫。所以,foo
、Foo
和 FOO
視為不同的識別字。在實務上,大部分的識別字會用小寫字母。全大寫字母僅用於常數 (constant) 和列舉 (enumeration)。按照 C 社群的慣例,幾乎不用 CapitalCase
來寫識別字。
即使 C 語言會區分大小寫,我們也不應濫用這項特性,以免寫出難以維護的程式碼。
空白 (Spaces)、縮進 (Indentations)、排版 (Code Formatting)
C 語言對於空白、縮進等版面安排相當自由,並沒有 Python 那種強制縮進的規則。甚至還有故意寫出難以閱讀的 C 程式碼的比賽,像是 IOCCC (The International Obfuscated C Code Contest)。但我們仍然鼓勵讀者在寫 C 程式碼時排版程式碼,或是使用程式碼自動重排軟體。因為整齊的程式碼對於日後維護會有所幫助。
由於 C 程式碼在編譯後就會轉成機械碼,對 C 程式碼刻意做縮小化 (minification) 只會影響到編譯程式碼的時間,對程式執行速度沒有幫助。所以這是沒有意義的行為。
註解 (Comments)
程式碼中的註解不會執行,所以可寫入一般文字。C 語言有兩種風格的註解。ANSI C 是使用一對 /*
和 */
把註解文字包起來,像是以下範例:
/* Some comment. */
由於傳統的註解能夠跨越多行,故未完全淘汰。
在 C99 後,引入單行註解。單行註解以 //
開頭,之後同一行內的所有文字皆視為註解。像是以下範例:
// Some single-line comment.
單行註解比較易寫,但無法跨越多行,所以未取代傳統的註解。
除了這兩種正統的註解外,還有另一種利用巨集來註解掉整段程式碼的手法:
#if 0
printf("It won't compile\n");
#endif
這樣操作的原理在於巨集會在編譯前就執行,相關程式碼會被抹去,等同於這段程式碼不存在。
註解的用途是記錄程式人撰寫程式碼時的意圖或想法。對於教學用的程式,註解可放入教學用文字。
有時候我們會用註解暫時隱藏掉一段程式碼,防止該段程式碼編譯及運作。例如,我們在除錯時,利用註解遮蔽掉可能有問題的程式碼,然後逐步縮小註解的範圍,直到找到錯誤為止。
引入函式庫
C 語言的語法刻意保持精簡,大部分的功能由函式庫來實現。甚至標準輸出入這種基本的功能也是藉由函式庫來實現。所以,學 C 語法不會花太久時間,但要寫到熟練則需要反覆練習。
引入函式庫的語法是 #include
。該語法是一種巨集 (macro),但我們一開始不需要在意巨集的寫法,因為 #include
敘述的寫法是固定的。
引入函式庫時,有兩種寫法。一種是以一對角括號 <
和 >
包住函式庫名稱。一種則是用一對分號 "
和 "
包住函式庫名稱。C 語言沒有強制要使用那一種方式。
一般來說,使用標準函式庫或外部函式庫時,使用角括號:
#include <stdlib.h>
使用內部函式庫時,則使用分號:
#include "mylib.h"
當使用分號引用函式庫時,C 編譯器會優先從 C 程式碼所在的路徑找標頭檔,故可用來引用內部函式庫。
主函式 (Main Function)
主函式是 C 程式的起始點,一般的 C 語言皆有主函式。但在嵌入式系統或是作業系統本身的 C 程式碼中,主函式則不是必需的。
標準 C 的主函式名稱固定為 main
。Windows C 則有不同的主函式名稱。但 Windows C 有很多非標準 C 的特性,只有要寫 Windows C 時再去學習即可。
如果該 C 程式不需要接收命令列參數,則使用以下方式來寫:
int main(void)
{
/* Implement your code here. */
}
若該 C 程式需要接收命令列參數,則改用以下方式來寫:
int main(int argc, char *argv[])
{
/* Implement your code here. */
}
除了命令列參數外,如果想要接收環境變數,可以用以下方式改寫:
int main(int argc, char *argv[], char *env[])
{
/* Implement your code here. */
}
現在這種寫法比較少見,因為標準 C 可用 getenv() 函式取得環境變數,沒有必要再用這種寫法。
至於以下寫法是錯誤的:
void main(void)
{
/* DON'T USE THIS IN PRODUCTION CODE. */
}
雖然 Visual C++ 可編譯通過,但這種寫法並非標準 C 的一部分,故不建議使用。
表達式 (Expression) 和敘述 (Statement)
表達式會回傳值,像是 "Hello World\n"
是表達式,該表達式是 "Hello World"
字串再附帶一個換行符號。而敘述代表單一指令,像是 printf("Hello World\n");
是一條敘述。C 語言的單行敘述會用 ;
結尾。
C 程式由一條條敘述組成,預設情形下,程式會由上往下依序執行敘述。但我們有許多改變程式行進流程的方式,像是控制結構或函式呼叫等。
離開狀態 (Exit Status)
我們在程式結束時回傳 0
:
return 0;
這代表程式正常結束。
C 程式在結束時,會向系統回傳一個整數,用來代表程式的狀態。一般來說,回傳 0
代表程式正常結束,回傳非零值 1
代表程式異常結束。
有些程式會用不同的回傳值表示不同的異常狀態,但除了回傳 0
表示正常結束以及回傳 1
表示異常結束外,目前 C 語言對其他的回傳值沒有共識。也就是說,用回傳值來判斷程式狀態不是很牢靠,我們也不鼓勵讀者使用複雜的回傳值來表示程式的離開狀態。
C 執行期函式庫
雖然 C 程式表面上不需要虛擬機器,但其實 C 程式仍然有執行期函式庫。像是 C 程式具有主函式,且主函式會回傳離開狀態,C 程式需要執行期函式庫來處理主函式。
C 語言的執行期函式庫很小,通常是以組合語言來撰寫,會在編譯 C 程式碼時一併加入 C 程式中,所以不需要另外安裝虛擬機器。C 語言的運行期函式庫稱為 crt0。
MSVC (Visual C++) 在不同版本的 C 編譯器使用不同的執行階段函式庫,所以在 Windows 上安裝以 C 撰寫的應用程式時,有時會需要安裝特定版本的執行階段函式庫。在 MSVC 中的 C 執行階段函式庫稱為 Visual C++ 可轉散發套件。
C 專案
C 語言本身沒有專案的概念,現今所見的 C 專案是利用社群開發工具來管理 C 程式碼的方式。剛開始學 C 語言時,建議直接用 IDE 管理 C 程式碼。等學一段時間後,再開始學習用開發工具建立 C 專案。筆者在這裡和這裡有更多說明,歡迎有需要的讀者前往閱讀。