Loading AI tools
通用程式語言 来自维基百科,自由的百科全书
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語言包括如下文法:
//
開頭)和多行注釋(以/*
開始,以*/
結束);int
)、浮點數(float
和double
)、字元(char
)、枚舉enum
等;int numbers[5] = {1, 2, 3, 4, 5};
struct
)、聯合(union
);if
、else
)、迴圈語句(for
、while
、do-while
)等;goto
、break
和continue
來實現程式塊之間的跳轉,這和組合語言的jmp
關鍵字有一定相似處;int *array = (int *)malloc(5 * sizeof(int)); // 分配一個包含五個整數的數組
free(array); // 釋放使用malloc分配的內存
現在廣泛被編程初學者使用的"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年的修正版中
在C99中增加了六個函式庫
工具軟體可以幫助程式設計者避免一些程式中潛藏或容易出現的問題,例如常會造成程式未預期動作或是執行期錯誤的程式碼。
許多語言都有自動原始碼檢查及審計工具,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]。