C語言

通用編程語言

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

C語言
C程序設計語言》,第一部介紹C語言的書籍
編程範型程序式指令式編程過程式)、結構化編程
設計者丹尼斯·里奇(Dennis Ritchie)
實作者丹尼斯·里奇(Dennis Ritchie)和肯·湯普遜(Ken Thompson)
面市時間1972年,​52年前​(1972
當前版本
  • C18 (C語言標準) (2018年6月;穩定版本)
  • C23 (2020年12月11日;預覽版本)[1][2]
編輯維基數據鏈接
型態系統靜態, 弱類型, 明示英語Manifest typing, 名稱英語Nominal type system
操作系統跨平台
網站www.iso.org/standard/74528.html 編輯維基數據鏈接
主要實作產品
ClangGCCMSVCTurbo CWatcom C
啟發語言
BBCPLCPL)、ALGOL 68[3]組合語言PL/IFORTRAN
影響語言
大量
如:awkBitC英語BitCcshC++C#
DJavaJavaScriptObjective-CPerlPHPRust

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

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

概述 編輯

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

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

C語言也具有以下的特性:[6]

  • 基本數據類型包括字符、整型和浮點數等。另外也有派生的各種數據類型,如指針數組、結構和聯合。
  • 部份的變量類型可以轉換,例如整數型和字符型變量。
  • 透過指標(pointer),C語言可以容易的對記憶體進行低階控制。
  • 不同的變數類型可以用結構體(struct)組合在一起。
  • 具有基本的控制流:語句組、條件判斷、多路選擇、循環等。
  • 函數可以返回各種數據類型的值,並且都可以遞歸調用。每次調用函數會重新創建變量。
  • C語言只有32個保留字(reserved keywords),使變量、函數命名有更多彈性。
  • 編譯預處理(preprocessor)讓C語言的編譯更具有彈性。

歷史 編輯

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語言至今經歷了幾次標準更新,誕生了C99C11和目前最新的標準C18。C語言標準的下一次更新C2x目前正在起草中。

語法 編輯

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

  1. 作為一種指令式編程語言,C語言使用語句執行操作。最常見的語句是表達式語句,由一個表達式後加一個分號組成,可以令系統調用函數和為變量賦值;
  2. 註釋: C語言支持單行注釋(以//開頭)和多行注釋(以/*開始,以*/結束);
  3. 數據類型: 基本的數據類型包括整數(int)、浮點數(floatdouble)、字符(char)、枚舉enum等;
  4. 數組: 數組是一組相同類型的數據元素的集合。使用以下方法初始化一個五個元素的整數數組:
    int numbers[5] = {1, 2, 3, 4, 5};
    
  5. 封裝結構:結構(struct)、聯合(union);
  6. 結構化編程和控制結構: C語言包括條件語句(ifelse)、循環語句(forwhiledo-while)等;
  7. 跳轉語句:C語言允許使用跳轉關鍵字gotobreakcontinue來實現程序塊之間的跳轉,這和匯編語言的jmp關鍵字有一定相似處;
  8. 函數: C語言中的函數是程序的基本模塊,可以自定義函數並在程序中調用;
  9. 靈活且靠近底層的內存控制機制:C程序員可以自由選擇分配何種內存,以及分配多大的內存,如如下代碼所示:
    int *array = (int *)malloc(5 * sizeof(int)); // 分配一個包含五個整數的數組
    free(array); // 釋放使用malloc分配的內存
    

Hello World 程序 編輯

 
Brian Kernighan於1978年親筆書寫的「Hello World」程序
"對於所有語言的初學者來說,編寫的第一個程序幾乎都是相同的,即『請打印出下列內容 hello, world』"
– 《C程序設計語言》[6]

現在廣泛被編程初學者使用的"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標準中:

文件 簡介說明
<assert.h> 斷言相關
<ctype.h> 字符類型判斷
<errno.h> 標準報錯機制
<float.h> 浮點運算
<limits.h> 各種體系結構限制
<locale.h> 本地化接口
<math.h> 數學函數
<setjmp.h> 跨函數跳轉
<signal.h> 信號(類似UNIX信號定義,但是差很遠)
<stdarg.h> 可變參處理
<stddef.h> 一些標準宏定義
<stdio.h> 標準I/O庫
<stdlib.h> 標準工具庫函數
<string.h> ASCII字符串及任意內存處理函數
<time.h> 時間相關

在94年的修正版中

  • <iso646.h>
  • <wchar.h>
  • <wctype.h>

在C99中增加了六個函式庫

  • <complex.h>
  • <fenv.h>
  • <inttypes.h>
  • <stdbool.h>
  • <stdint.h>
  • <tgmath.h>

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

工具軟體 編輯

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

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

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

使用像ValgrindIBM Rational Purify英語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]

參見 編輯

註腳 編輯

註解 編輯

參考資料 編輯

  1. ^ History of C. 2020年12月13日 [2020年10月24日] (英語). 
  2. ^ Programming languages — C (PDF). 2020年12月11日 [2020年12月17日] (英語). 
  3. ^ Ritchie, Dennis M. The Development of the C Language. January 1993 [2008-01-01]. (原始內容存檔於2015-02-03). The scheme of type composition adopted by C owes considerable debt to Algol 68, although it did not, perhaps, emerge in a form that Algol's adherents would approve of. 
  4. ^ TIOBE Programming Community Index [TIOBE編程社區指數]. 2012 [2012-11-03]. (原始內容存檔於2018-12-25) (英語). 
  5. ^ Brian W. Kernighan and Dennis M. Ritchie. The C programming Language. Prentice-Hall. 1988. ISBN 0-13-110362-8 (英語). In C, all function arguments are passed ``by value. 
  6. ^ 6.0 6.1 6.2 Dennis M. Ritchie,Brian W. Kernighan. C程序设计语言. 北京: 機械工業出版社. 2004年1月 [2020-06-10]. ISBN 9787111128069 (中文). 
  7. ^ Stephen Prata. C Primer Plus(第5版). 北京: 人民郵電出版社. 2005年2月: 3-4 [2020-07-15]. ISBN 9787115130228 (中文). 
  8. ^ ISO/IEC JTC1/SC22/WG14 - C. [2022-04-02]. (原始內容存檔於2018-02-12) (英語). 
  9. ^ check-pointers, Qcheck-pointers. Intel. [2021-06-01]. (原始內容存檔於2021-02-15) (英語). 
  10. ^ ISO/IEC 9899:2018 (PDF). [2020-06-10]. (原始內容存檔 (PDF)於2020-07-22). 
  11. ^ Can I write "void main()"?頁面存檔備份,存於網際網路檔案館)The definition void main() { /* ... */ } is not and never has been C++, nor has it even been C.
  12. ^ 用 C99 进行开放源代码的开发. [2011-01-21]. (原始內容存檔於2011-08-12). 
  13. ^ 「The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters: int main(void) { /* ... */ } or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared): int main(int argc, char *argv[]) { /* ... */ } or equivalent; or in some other implementation-defined manner.」,引自ISO/IEC 9899:1999, Section 5.1.2.2.1 Program startup
  14. ^ 「An implementation shall not predefine the main function. This function shall not be overloaded. It shall have a return type of type int, but otherwise its type is implementation-defined. All implementations shall allow both of the following definitions of main: int main() { /* ... */ } and int main(int argc, char* argv[]) { /* ... */ }.」,引自 ISO/IEC 14882, 第一版(1998)、第二版(2003)與第三版(2011), section 3.6.1 Main function

參考資料 編輯

外部連結 編輯