C語言

C語言（英語：C Language）是一種通用的、過程式編程程式語言，支持結構化編程、詞法作用域和遞歸，使用靜態類型系統，並且廣泛用於系統軟體與應用軟體的開發。

C語言於1969年至1973年間，為了移植與開發UNIX作業系統，由丹尼斯·里奇與肯·湯普遜，以B語言為基礎，在貝爾實驗室設計、開發出來。二十世紀八十年代，C語言應用日漸廣泛。為了避免各開發廠商用的C語言的語法產生差異，美國國家標準局為C語言訂定了一套完整的國際標準語法，稱為ANSI C，作為C語言的標準。與此同時，國際標準化組織也接受該標準為國際標準。因此，ANSI C也同時被稱為ISO C。二十世紀八十年代至今的有關程式開發工具，一般都支持符合ANSI C的語法。

C語言具有高效、靈活、功能豐富、表達力強和較高的可移植性等特點，在程式設計中備受青睞，成為最近25年使用最為廣泛的程式語言。目前，C語言編譯器普遍存在於各種不同的作業系統中，例如Microsoft Windows、macOS、Linux、Unix等。C語言的設計影響了眾多後來的程式語言，例如C++、Objective-C、Java、C#等。現行的許多軟體都是由C語言或者其影響和衍生的程式語言開發出來的。

## 概述

與ALGOL一族的大多數過程式程式語言類似，C語言是一個有結構化程式設計、具有變量作用域（variable scope）以及遞迴功能的程序式語言。其採用的靜態類型系統可以防止無意的程序設計操作。C語言中所有的可執行代碼都被包含在子程序（函數）裡。其傳遞參數均是以值傳遞（pass by value），另外也可以傳遞指針（a pointer passed by value）。C語言是自由形式語言，即其原始碼的縮進並不影響程序的功能，而是使用分號作為語句的結尾，花括號來表示代碼塊。

由於C語言的語言規模較小，若干高層的機制需要使用定義的函數來提供。比如，C語言並沒有直接處理複合對象（例如字串、集合、列表、陣列等）的操作，也沒有對於存儲器分配工具和記憶體回收工具的直接定義，同時也本身不具有輸入和輸出以及文件訪問的方法。然而，使用者定義的函數和C語言標準庫中的函數為這些高層的機制提供了可能性。

C語言也具有以下的特性：

C語言提供字元、整數、浮點數等基本資料型別，並支援指標、陣列、結構體與聯合體等衍生資料型別。其型別系統允許部分資料型別之間進行轉換，例如整數與字元型別之間的轉換。

作為一種接近硬體層的程式語言，C語言透過指標機制提供對記憶體的直接操作能力，使程式設計者能夠進行較低階的系統控制。同時，不同型別的資料也可以透過結構體組合為更複雜的資料結構。

在程式控制方面，C語言提供條件判斷、多路選擇、迴圈等基本控制結構，並以函式作為程式組織與重複利用的核心機制。函式可以回傳不同型別的資料，也支援遞迴呼叫，每次呼叫時都會建立獨立的區域變數環境。

此外，C語言的保留字數量相對較少，使變數與函式命名具有較大的彈性。其前置處理器（preprocessor）則提供巨集展開、條件編譯與檔案引用等功能，進一步增強程式開發與編譯過程的靈活性。

## 歷史

20世紀70年代，肯·湯姆森為了使其設計的Unix系統更加高效，使用B語言的變種（即C語言）在DEC PDP-7電腦上重寫了Unix。C語言中許多重要概念來源於BCPL語言，其對C語言的影響也間接地來源於B語言。在1978年，丹尼斯·里奇和布萊恩·柯林漢合作出版了《C程序設計語言》第一版，事實上即為K&R C標準。1983年，為了制定一個獨立於具體機器且無歧義的C語言標準，美國國家標準協會成立了一個委員會，並在1988年完成了該標準的制定，即ANSI C。此標準同時被國際標準化組織所採納，也被稱作ISO C。

其後，C語言至今經歷了幾次標準更新，誕生了C99、C11、C18和目前最新的標準C23。C語言標準的下一次更新C2Y目前正在起草中。

## 語法

C語言的語法相對簡潔而直接。C語言的形式文法由國際標準化組織所制定。簡單來說，C語言包括如下文法：

- 作為一種指令式程式語言，C語言使用語句執行操作。最常見的語句是表達式語句，由一個表達式後加一個分號組成，可以令系統調用函數和為變量賦值。
- 註釋： C語言支持單行注釋（以`//`開頭）和多行注釋（以`/*`開始，以`*/`結束）。
- 數據類型：基本的數據類型包括整數（`int`和`long long`）、浮點數（`float`、`double`和`long double`）、字符（`char`）、枚舉`enum`等。
- 陣列：陣列是一組相同類型的數據元素的集合。使用以下方法初始化一個五個元素的整數陣列：

```c
int numbers[5] = {1, 2, 3, 4, 5};
```

- 封裝結構：結構（`struct`）、聯合（`union`）。
- 結構化編程和控制結構： C語言包括條件語句（`if`、`else`、`switch-case`）、循環語句（`for`、`while`、`do-while`）等。
- 跳轉語句：C語言允許使用跳轉關鍵字`goto`、`break`和`continue`來實現程序塊之間的跳轉，這和匯編語言的`jmp`關鍵字有一定相似處。
- 函數： C語言中的函數是程序的基本模塊，可以自定義函數並在程序中調用。
- 靈活且靠近底層的記憶體控制機制：C程序員可以自由選擇分配何種記憶體，以及分配多大的記憶體，如下代碼所示：

```c
#include <stdlib.h>     //调用分配内存需要的函数
int *array = (int *)malloc(5 * sizeof(int)); // 分配一個包含五個整數的數組
free(array); // 釋放使用malloc分配的內存
```

## Hello World 程序

<image>

"對於所有語言的初學者來說，編寫的第一個程序幾乎都是相同的，即『請列印出下列內容 hello, world』"

現在廣泛被編程初學者使用的"hello, world"程序實例最初就是出現在《C程序設計語言》第一版中。下面是一個在標準輸出設備（stdout）上列印出 "Hello, world!" 字串的簡單程式。類似的程式，通常作為初學程式語言時的第一個程式：

```c
#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}
```

其中只有int，void，return為C語言的關鍵字，預處理器會將`#include <stdio.h>`替換為stdio.h文件的內容。

main函數是C語言程序的入口點。

`"Hello, world!\n"`中的`\n`是一個轉義字符，形式為`\`加上一個字符。所起的作用在ASCII碼中規定。

`printf`是聲明於stdio.h的函數，其參數為格式化字符串。

`return 0;`代表程式返回代表正常退出的退出狀態，現在常見的編譯器會自動補上。

關於main函數的定義，`void main()` 的用法並不是任何標準制定的。C語言標準語法是 `int main()`，任何實現都必須支持`int main(void) { /* ... */ }`和`int main(int argc, char* argv[]) { /* ... */ }`。在 C++ 標準中，main的標準型態應是int，否則類型是由實現定義的。任何實現都必須支持`int main() { /* ... */ }`和`int main(int argc, char* argv[]) { /* ... */ }`。

## 記憶體管理

C語言的特色之一是：程式設計師必須親自處理記憶體的分配細節。語言不負責記憶體邊界檢查，這是因為在運行時進行記憶體邊界檢查會造成性能問題，與UNIX哲學不符。此特性容易導致緩衝區溢出問題。然而，部分編譯器（如英特爾編譯器）會出於安全性的考量，提供方法以進行運行時記憶體邊界檢查。

大多數C語言實現使用棧（Stack）來保存函數返回地址／棧幀基址、完成函數的參數傳遞和函數局部變量的存儲。然而，在部分極特殊的平台上，使用棧並不能獲得最大效率。此時的實現由編譯器決定。

如果程序需要在運行的過程中動態分配記憶體，可以利用堆（Heap）來實現。

基本上C程序的元素存儲在記憶體的時候有3種分配策略：

- 靜態分配

如果一個變量聲明為全局變量或者是函數的靜態變量，這個變量的存儲將使用靜態分配方式。靜態分配的記憶體一般會被編譯器放在數據段或代碼段來存儲，具體取決於實現。這樣做的前提是，在編譯時就必須確定變量的大小。

以IA32的x86平台及gcc編譯器為例，全局及靜態變量放在數據段的低端；全局及靜態常量放在代碼段的高端。

- 自動分配

函數的自動局部變量應該隨著函數的返回會自動釋放（失效），這個要求在一般的體系中都是利用棧（Stack）來滿足的。相比於靜態分配，這時候，就不必絕對要求這個變量在編譯時就必須確定變量的大小，運行時才決定也不遲，但是C89仍然要求在編譯時就要確定，而C99放鬆了這個限制。但無論是C89還是C99，都不允許一個已經分配的自動變量運行時改變大小。

所以說C函數永遠不應該返回一個局部變量的地址。

要指出的是，自動分配也屬於動態分配，甚至可以用alloca函數來像分配堆（Heap）一樣進行分配，而且釋放是自動的。

- 動態分配

還有一種更加特殊的情況，變量的大小在運行時有可能改變，或者雖然單個變量大小不變，變量的數目卻有很大彈性，不能靜態分配或者自動分配，這時候可以使用堆（Heap）來滿足要求。ANSI C定義的堆操作函數是malloc、calloc、realloc和free。

使用堆（Heap）記憶體將帶來額外的開銷和風險。

## 庫

C語言的標準文檔要求了一個平台移植C語言的時候至少要實現的一些功能和封裝的集合，稱為「標準庫」，標準庫的聲明頭部通過預處理器命令#include進行引用。

在C89標準中：

文件｜簡介說明

斷言相關

字符類型判斷

標準報錯機制

浮點運算

各種體系結構限制

本地化接口

數學函數

跨函數跳轉

信號（類似UNIX的信號定義，但是差很遠）

可變參處理

一些標準宏定義

標準I/O庫

標準工具庫函數

ASCII字串及任意記憶體處理函數

`<time.h>`｜時間相關

在94年的修正版中

在C99中增加了六個函式庫

以上是C語言的標準。各個系統各自又對C庫函數進行的各種擴充，就浩如煙海了。如POSIX C、GNU C等。

## 工具軟體

工具軟體可以幫助程式設計者避免一些程式中潛藏或容易出現的問題，例如常會造成程式未預期動作或是執行期錯誤的程式碼。

許多語言都有自動源代碼檢查及審計工具，C語言也有類似工具，像是Lint。可以在程式剛寫好時用Lint找出可能有問題的程式，通過Lint後再用C編譯器進行編譯，許多編譯器也可以設定是否要針對一些可能有問題的程式碼提出警告。MISRA C是一套針對嵌入式系統的法則，可主要也是避免一些可能有問題的程式碼。

也有一些編譯器、程式庫或作業系統可以處理一些非標準C語言的功能，例如邊界值檢查、緩存溢出偵測、序列化及自動垃圾回收功能。

使用像Valgrind或IBM Rational Purify等軟體工具，或者連結有特別malloc函式的程式庫，有助於找出一些運行期記憶體使用的問題。

## 外部連結

- Coding Programmer Page / C Library Reference and Examples (english)
- GCC 首頁
- GLIBC2 首頁
- Visual Studio Express 首頁
- ISO/IEC 9899。C99標準的官方網站。目前（2020年）可直接下載的標準文本是 ISO/IEC 9899:202x。

## 基本資料

- 日語原文：C語言
- 域：在第一部介紹C語言的書籍《C程序設計語言》的初版封面上使用的C標識
- 編程范型：程序式指令式編程（過程式）、結構化編程
- 發行時間：1972年
- 設計者：丹尼斯·里奇（Dennis Ritchie）
- 開發者：丹尼斯·里奇（Dennis Ritchie）和肯·湯普遜（Ken Thompson）
- 軟體版本週期： ISO/IEC 9899:2018（C18）
- 軟體版本週期：2018年6月
- 類型系統：靜態, 弱類型, 明示類型, 名稱類型系統
- 軟體實現：Clang、GCC、MSVC、Turbo C、Watcom C
- 受影響於：B（BCPL、CPL）、ALGOL 68、組合語言、PL/I、FORTRAN
- 施影響於：、如：awk、BitC、csh、C++、C#、D、Java、JavaScript、Objective-C、Perl、PHP、Rust等
- 作業系統：跨平台
- wikibooks：C Programming