C語言(英語:C Language)是一種通用的、過程式編程程式語言,支持結構化編程、詞法作用域和遞歸,使用靜態類型系統,並且廣泛用於系統軟件與應用軟件的開發。
![]() |
![]() 《C程序設計語言》,第一部介紹C語言的書籍 | |
編程範型 | 程序式指令式編程(過程式)、結構化編程 |
---|---|
設計者 | 丹尼斯·里奇(Dennis Ritchie) |
實作者 | 丹尼斯·里奇(Dennis Ritchie)和肯·湯普遜(Ken Thompson) |
面市時間 | 1972年 |
當前版本 | |
型態系統 | 靜態, 弱類型, 明示, 名稱 |
操作系統 | 跨平台 |
網站 | www |
主要實作產品 | |
Clang、GCC、MSVC、Turbo C、Watcom C | |
啟發語言 | |
B(BCPL、CPL)、ALGOL 68[3]、組合語言、PL/I、FORTRAN | |
影響語言 | |
大量, 如:awk、BitC、csh、C++、C#、 D、Java、JavaScript、Objective-C、Perl、PHP、Rust等 | |
|
C語言於1969年至1973年間,為了移植與開發UNIX作業系統,由丹尼斯·里奇與肯·湯普遜,以B語言為基礎,在貝爾實驗室設計、開發出來。二十世紀八十年代,C語言應用日漸廣泛。為了避免各開發廠商用的C語言的語法產生差異,美國國家標準局為C語言訂定了一套完整的國際標準語法,稱為ANSI C,作為C語言的標準。與此同時,國際標準化組織也接受該標準為國際標準。因此,ANSI C也同時被稱為ISO C。二十世紀八十年代至今的有關程式開發工具,一般都支持符合ANSI C的語法。
C語言具有高效、靈活、功能豐富、表達力強和較高的可移植性等特點,在程式設計中備受青睞,成為最近25年使用最為廣泛的編程語言[4]。目前,C語言編譯器普遍存在於各種不同的操作系統中,例如Microsoft Windows、macOS、Linux、Unix等。C語言的設計影響了眾多後來的程式語言,例如C++、Objective-C、Java、C#等。現行的許多軟件都是由C語言或者其影響和衍生的編程語言開發出來的。
概述
與ALGOL一族的大多數過程式編程語言類似,C語言是一個有結構化程式設計、具有變量作用域(variable scope)以及遞迴功能的程序式語言。其採用的靜態類型系統可以防止無意的程序設計操作。C語言中所有的可執行代碼都被包含在子程序(函數)裡。其傳遞參數均是以值傳遞(pass by value)[5],另外也可以傳遞指針(a pointer passed by value)。C語言是自由形式語言,即其源代碼的縮進並不影響程序的功能,而是使用分號作為語句的結尾,花括號來表示代碼塊。
由於C語言的語言規模較小,若干高層的機制需要使用定義的函數來提供。比如,C語言並沒有直接處理複合對象(例如字符串、集合、列表、數組等)的操作,也沒有對於存儲器分配工具和內存回收工具的直接定義,同時也本身不具有輸入和輸出以及文件訪問的方法。然而,用戶定義的函數和C語言標準庫中的函數為這些高層的機制提供了可能性。[6]
C語言也具有以下的特性:[6]
歷史
20世紀70年代,肯·湯姆森為了使其設計的Unix系統更加高效,使用B語言的變種(即C語言)在DEC PDP-7計算機上重寫了Unix。C語言中許多重要概念來源於BCPL語言,其對C語言的影響也間接地來源於B語言。在1978年,丹尼斯·里奇和布萊恩·柯林漢合作出版了《C程序設計語言》第一版,事實上即為K&R C標準[7]。1983年,為了制定一個獨立於具體機器且無歧義的C語言標準,美國國家標準協會成立了一個委員會,並在1988年完成了該標準的制定,即ANSI C。此標準同時被國際標準化組織所採納,也被稱作ISO C。
其後,C語言至今經歷了幾次標準更新,誕生了C99、C11和目前最新的標準C18。C語言標準的下一次更新C2x目前正在起草中。
語法
![]() | 此章節需要擴充。 |
C語言的語法相對簡潔而直接。C語言的形式文法由國際標準化組織所制定。[8]簡單來說,C語言包括如下文法:
- 作為一種指令式編程語言,C語言使用語句執行操作。最常見的語句是表達式語句,由一個表達式後加一個分號組成,可以令系統調用函數和為變量賦值;
- 註釋: C語言支持單行注釋(以
//
開頭)和多行注釋(以/*
開始,以*/
結束); - 數據類型: 基本的數據類型包括整數(
int
)、浮點數(float
和double
)、字符(char
)、枚舉enum
等; - 數組: 數組是一組相同類型的數據元素的集合。使用以下方法初始化一個五個元素的整數數組:
int numbers[5] = {1, 2, 3, 4, 5};
- 封裝結構:結構(
struct
)、聯合(union
); - 結構化編程和控制結構: C語言包括條件語句(
if
、else
)、循環語句(for
、while
、do-while
)等; - 跳轉語句:C語言允許使用跳轉關鍵字
goto
、break
和continue
來實現程序塊之間的跳轉,這和匯編語言的jmp
關鍵字有一定相似處; - 函數: C語言中的函數是程序的基本模塊,可以自定義函數並在程序中調用;
- 靈活且靠近底層的內存控制機制:C程序員可以自由選擇分配何種內存,以及分配多大的內存,如如下代碼所示:
int *array = (int *)malloc(5 * sizeof(int)); // 分配一個包含五個整數的數組 free(array); // 釋放使用malloc分配的內存
Hello World 程序
![Thumb](http://upload.wikimedia.org/wikipedia/commons/thumb/2/21/Hello_World_Brian_Kernighan_1978.jpg/640px-Hello_World_Brian_Kernighan_1978.jpg)
現在廣泛被編程初學者使用的"hello, world"程序實例最初就是出現在《C程序設計語言》第一版中。下面是一個在標準輸出設備(stdout)上打印出 "Hello, world!" 字串的簡單程式。類似的程式,通常作為初學程式語言時的第一個程式:
#include <stdio.h>
int main(void) {
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的函數,關於printf的更多細節,參見printf;
關于格式化字符串的更多信息,參見格式化字符串。
內存管理
C語言的特色之一是:程序員必須親自處理內存的分配細節。語言不負責內存邊界檢查,這是因為在運行時進行內存邊界檢查會造成性能問題,與UNIX哲學不符。此特性容易導致緩衝區溢出問題。然而,部分編譯器(如英特爾編譯器)會出於安全性的考量,提供方法以進行運行時內存邊界檢查[9]。
大多數C語言實現使用棧(Stack)來保存函數返回地址/棧幀基址、完成函數的參數傳遞和函數局部變量的存儲。然而,在部分極特殊的平台上,使用棧並不能獲得最大效率。此時的實現由編譯器決定[10]。 如果程序需要在運行的過程中動態分配內存,可以利用堆(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標準中:
在94年的修正版中
- <iso646.h>
- <wchar.h>
- <wctype.h>
在C99中增加了六個函式庫
- <complex.h>
- <fenv.h>
- <inttypes.h>
- <stdbool.h>
- <stdint.h>
- <tgmath.h>
工具軟體
工具軟體可以幫助程式設計者避免一些程式中潛藏或容易出現的問題,例如常會造成程式未預期動作或是執行期錯誤的程式碼。
許多語言都有自動源代碼檢查及審計工具,C語言也有類似工具,像是Lint。可以在程式剛寫好時用Lint找出可能有問題的程式,通過Lint後再用C編譯器進行編譯,許多編譯器也可以設定是否要針對一些可能有問題的程式碼提出警告。MISRA C是一套針對嵌入式系統的法則,可主要也是避免一些可能有問題的程式碼。
也有一些編譯器、程式庫或作業系統可以處理一些非標準C語言的功能,例如邊界值檢查、緩存溢出偵測、序列化及自動垃圾回收功能。
使用像Valgrind或IBM Rational Purify等軟體工具,或者連結有特別malloc函式的程式庫,有助於找出一些運行期記憶體使用的問題。
經典錯誤
「void main()」的用法並不是任何標準制定的[11][12]。 C語言標準語法是「int main()」,任何實現都必須支持int main(void) { /* ... */ }
和int main(int argc, char* argv[]) { /* ... */ }
[13]。 在 C++ 標準中,main的標準型態應是int,否則類型是由實現定義的。任何實現都必須支持int main() { /* ... */ }
和int main(int argc, char* argv[]) { /* ... */ }
[14]。
參見
註腳
參考資料
外部連結
Wikiwand in your browser!
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.