本文採用電腦和資訊科技組全文轉換 [檢視] • [編輯] • [強制刷新]
以下為本條目單獨的全文轉換,請從本頁面進行編輯:
| C語言 | |
|---|---|
| 程序式 | |
|
面市時間
|
1972年 |
|
設計者
|
丹尼斯·里奇(Dennis Ritchie) |
|
實作者
|
丹尼斯·里奇(Dennis Ritchie)和肯·湯普遜(Ken Thompson) |
| 最近釋出日期 | C99 (2000年3月) |
|
啟發語言
|
B語言、組合語言 |
|
影響語言
|
awk, BitC, csh, C++, C#, Concurrent C, D, Java, JavaScript, Objective-C, Perl, PHP |
| 跨平台 | |
C語言,是一種通用的、程序式的程式語言,廣泛用於系統與應用軟體的開發。具有高效、靈活、功能豐富、表達力強和較高的移植性等特點,在程式設計師中備受青睞。
C語言是由UNIX的研製者丹尼斯·里奇(Dennis Ritchie)和肯·湯普遜(Ken Thompson)於1970年研製出的B語言的基礎上發展和完善起來的。目前,C語言編譯器普遍存在於各種不同的作業系統中,例如UNIX、MS-DOS、Microsoft Windows及Linux等。C語言的設計影響了許多後來的程式語言,例如C++、Java、C#等。
後來於1980年代,為了避免各開發廠商用的C語言語法產生差異,由美國國家標準局(American National Standard Institution)為C語言訂定了一套完整的國際標準語法,稱為ANSI C,作為C語言的標準。1980年代至今的有關程式開發工具,一般都支援符合ANSI C的語法。
目錄 |
C語言是一個程式語言,設計目標是提供一種能以簡易的方式編譯、處理低階記憶體、產生少量的機器碼以及不需要任何執行環境支援便能執行的程式語言。C語言也很適合搭配組合語言來使用。儘管C語言提供了許多低階處理的功能,但仍然保持著良好跨平台的特性,以一個標準規格寫出的C語言程式可在許多電腦平台上進行編譯,甚至包含一些嵌入式處理器(微控制器或稱MCU)以及超級電腦等作業平台。
C語言的第一次發展在1969年到1973年之間。之所以被稱為「C」是因為C語言的很多特性是由一種更早的被稱為B語言的程式語言中發展而來。早期作業系統的核心大多由組合語言組成,隨著C語言的發展,C語言已經可以用來編寫作業系統的核心。1973年,Unix作業系統的核心正式用C語言改寫,這是C語言第一次應用在作業系統的核心編寫上。
1978年,丹尼斯·里奇(Dennis Ritchie)和Brian Kernighan合作出版了《C程式語言》的第一版。書中介紹的C語言標準也被C語言程式設計師稱作「K&R C」,第二版的書中也包含了一些ANSI C的標準。K&R C主要介紹了以下特色:
即使在後來ANSI C標準被提出的許多年後,K&R C仍然是許多編譯器的最低標準要求,許多老舊的編譯仍然執行K&R C的標準。
1989年,C語言被 ANSI 標準化(ANSI X3.159-1989)。標準化的一個目的是擴充套件K&R C。這個標準包括了一些新特性。在K&R出版後,一些新特性被非官方地加到C語言中。
在ANSI標準化自己的過程中,一些新的特性被加了進去。ANSI也規定了一套標準函式庫。ANSI ISO(國際標準化組織)成立 ISO/IEC JTC1/SC22/WG14 工作組,來規定國際標準的C語言。透過對ANSI標準的少量修改,最終透過了 ISO 9899:1990。隨後,ISO標準被 ANSI 採納。
傳統C語言到ANSI/ISO標準C語言的改進包括:
WG14工作小組之後又於1995年,對1985年頒布的標準做了兩處技術修訂(缺陷修復)和一個補充(擴充套件)。下面是 1995 年做出的所有修改:
在ANSI的標準確立後,C語言的規範在一段時間內沒有大的變動,然而C++在自己的標準化建立過程中繼續發展壯大。《標準修正案一》在1995年為C語言建立了一個新標準,但是只修正了一些C89標準中的細節和增加更多更廣得國際字符集支援。不過,這個標準引出了1999年ISO 9899:1999的發表。它通常被成為C99。C99被ANSI於2000年3月採用。
在C99中包括的特性有:
struct {int a[3],b;} hehe[] = { [0].a = {1}, [1].a = 2 };
struct {int a, b, c, d;} hehe = { .a = 1, .c = 3, 4, .b = 5} // 3,4 是对 .c,.d 赋值的
但是各個公司對C99的支援所表現出來的興趣不同。當GCC和其它一些商業編譯器支援C99的大部分特性的時候,微軟和Borland卻似乎對此不感興趣。
下面是一個在標準輸出裝置 (stdout) 上,印出 "Hello World" 字串的簡單程式,這樣子的程式,通常作為初學程式語言時的第一個程式:
#include <stdio.h> int main(void) { printf("Hello, world!\n"); return 0; }
C語言由函式和變數組成。C的函式就像是Fortran中的子程式和函式。
在C語言中,程式從main開始執行。main函式透過呼叫和控制其他函式進行工作。例如上面的printf。程式設計師可以自己寫函式,或從函式庫中呼叫函式。在上面的return 0;使得main返回一個值給呼叫程式的外殼,表明程式已經成功執行。
一個C語言的函式由返回值、函式名、參數列(或void表示沒有返回值)和函式體組成。函式體的語法和其它的複合的語句部分是一樣的。
C語言中的複合語句(或稱語句塊)的格式為:
{语句;语句;……}
複合語句可以使得幾個語句從文法上變成一個語句。
C語言有三種條件語句形式。兩種是if,另一種是switch。
兩種if包括:
if(運算式) 語句;
以及
if(運算式) 語句; else 語句;
運算式的值非零表示條件為真;如果條件為假,程式將跳過if處的語句,直接執行if後面的語句。但是如果if後面有else,則當條件為假時,程式跳到else處執行。if和else後面的語句可以是另個if語句,這種套疊式的結構,允許更複雜的邏輯控制流程得以實現。在一般情況下,else一定與最接近的if成對,必要時可用括弧{}越過此限制。比較下面兩種情況:
if(運算式) if (運算式) 語句; else 語句;
if(運算式){ if (運算式) 語句; } else 語句;
switch通常用於對幾種有明確值的條件進行控制。它要求的條件值通常是整數或字元。與switch搭配的條件轉移是case。使用case後面的標值,控制程式將跳到滿足條件的case處一直往下執行,直到語句結束或遇到break。通常可以使用default把其他例外的情況包含進去。如果switch語句中的條件不成立,控制程式將跳到default處執行;如果省略default子句,則直接執行下一語句。switch是可以巢狀的。
switch (<運算式>) { case <值1>: <語句> break; case <值2>: <語句> default : <語句> }
C語言有三種形式的迴圈語句:
do <语句> while (<表达式>); while (<表达式>) <语句>; for (<表达式1> ; <表达式2> ; <表达式3>) <语句>;
在while和do中,語句將執行到運算式的值為零時結束。在do...while語句中,迴圈體將至少被執行一次。這三種迴圈結構可以互相轉化:
for (<表达式1>; <表达式2>; <表达式3>) <语句>;
如果<語句>中不使用continue語句的話,相當於
<表达式1>; while (<表达式2>) { <语句>; <表达式3>; }
當迴圈條件一直為真時,將產生無窮迴圈。
跳轉語句包括四種:goto,continue,break和return。
goto語句是無條件轉移語句:
goto 標記;
標記必須在當前函數中定義,使用「標記:」的格式定義。程式將跳到標記處繼續執行。由於goto容易產生閱讀上的困難,所以應該儘量少用。
continue語句用在迴圈語句中,作用是結束當前一輪的迴圈,馬上開始下一輪迴圈。
break語句用在迴圈語句或switch中,作用是結束當前迴圈,跳到循環體外繼續執行。但是使用break只能跳出一層迴圈。在要跳出多重迴圈時,可以使用goto使得程式更為簡潔。
當一個函數執行結束後要返回一個值時,使用return。return可以跟一個運算式或變數。如果return後面沒有值,將執行不返回值。
| () [] -> . ! ++ -- (cast) | 括弧、成員、邏輯非、自加、自減、強制轉換 |
| ++ -- * & ~ ! + - sizeof | 單目運算符 |
| * / % | 算術運算符 |
| + - | 算術運算符 |
| << >> | 位運算符 |
| < <= > >= | 關聯運算符 |
| ==!= | 關聯運算符號 |
| & | 位與 |
| ^ | 位異或 |
| | | 位或 |
| && | 邏輯與 |
| || | 邏輯或 |
| ?: | 條件運算符 |
| = += -= *= /= %= <<= >>= &= |= ^= | 賦值運算符 |
| , | 順序運算符 |
比較特別的是,位元右移(>>)運算子可以是算術(左端補最高有效位)或是邏輯(左端補 0)位移。例如,將 11100011 右移 3 位元,算術右移後成為 11111100,邏輯右移則為 00011100。因算術位元右移較適於處理帶負號整數,所以幾乎所有的編譯器都是算術位元右移。
注意:以下是典型的資料位長和範圍。但是編譯器可能使用不同的資料位長和範圍。這取決於使用的編譯器。請參考具體的參考手冊。
在標頭檔<limits.h>和<float.h>中說明了基礎資料的長度。float,double和long double的範圍就是在IEEE 754標準中提及的典型資料。
| 關鍵字 | 位長 | 範圍 | printf chars |
|---|---|---|---|
char |
1 | -128..127(或0..255,與體系結構相關) | %c |
unsigned char |
1 | 0..255 | |
signed char |
1 | -128..127 | |
int |
2 or 4 |
-32768..32767 or -2147483648..2147483647 |
%i, %d |
unsigned int |
2 or 4 |
0..65535 or 0..4294967295 |
%u |
signed int |
2 or 4 |
-32768..32767 or -2147483648..2147483647 |
%i, %d |
short int |
2 | -32768..32767 | %hi |
unsigned short |
2 | 0..65535 | %hu |
signed short |
2 | -32768..32767 | |
long int |
4 | -2147483648..2147483647 | %li, %ld |
unsigned long |
4 | 0..4294967295 | %lu |
signed long |
4 | -2147483648..2147483647 | |
long long |
8 | -9223372036854775808..9223372036854775807 | %lli |
unsigned long long |
8 | 0..18446744073709551615 | %llu |
float |
4 | 3.4x10-38..3.4x10+38 (7 sf) | %f, %e, %g |
double |
8 | 1.7x10-308..1.7x10+308 (15 sf) | %f, %e, %g |
long double |
8 或以上 | 編譯器相關 | %Lf, %Le, %Lg |
如果一個變數名後面跟著一個有數字的中括弧,這個聲明就是陣列聲明。字串也是一種陣列。它們以ASCII的NUL作為陣列的結束。要特別注意的是,方括內的索引值是從0算起的。
例如:
上面最後一個例子創建了一個陣列,但也可以把它看成是一個多維陣列。注意陣列的下標從0開始。這個陣列的結構如下:
a[0][0] |
a[0][1] |
a[0][2] |
a[0][3] |
a[1][0] |
a[1][1] |
a[1][2] |
a[1][3] |
a[2][0] |
a[2][1] |
a[2][2] |
a[2][3] |
例子(*)創建了一個3*3的二維陣列,初始化時有些元素並未賦值。如下:
為0的位置的數值是隨機的。
如果一個變數宣告時在前面使用 * 號,表明這是個指標型變數。換句話說,該變數儲存一個位址,而 * 則是取內容運算子,意思是取這個內部記憶體位址里儲存的內容。指標是 C 語言區別於其他同時代高階語言的主要特徵之一。
指標是一把雙刃劍,許多操作可以透過指標自然的表達,但是不正確的或者過分的使用指標又會給程式帶來大量潛在的錯誤。
例如:
int *pi; /* 指向整型数据的指针变量 */ int *api[3];/* 由指向整型数据的指针构成的数组,长度为 3 */ char **argv; /* 指向一个字符指针的指针 */
儲存在指標中的位址所指向的數值在程式中可以由 * 讀取。例如,在第一個例子中, *pi 是一個整型資料。這叫做參照一個指標。
另一個運算符 &,叫做取位址運算符,它將返回一個變數、陣列或函式的儲存位址。因此,下面的例子:
int i, *pi; /* int and pointer to int */ pi = &i;
i 和 *pi 在程式中可以相互交替使用,直到 pi 被改變成指向另一個變數的位址。
C語言的字串其實就是char型陣列,所以使用字串並不需要參照庫。但是C標準庫確實包含了一些用於對字串進行操作的函數,使得它們看起來就像字串而不是陣列。使用這些函數需要參照標頭檔<string.h>。
在C語言中,輸入和輸出是經由標準函式庫中的一組函數來實現的。在ANSI/ISO C中,這些函數被定義在標頭檔<stdio.h>中。
有三個標準輸入/輸出是標準I/O函式庫預先定義的:
下面的這個例子顯示了一個過濾程式(filter program)是怎樣構成的。
#include <stdio.h> int main(void) { int c; while (1) { c = getchar(); if (c==EOF) { perror("getchar()"); return -1; } putchar(c); } return 0; }
C語言的特色之一是:程式設計師必須親自處理內部記憶體的分配細節。
C語言使用棧來保存函式返回位址/棧禎基址、完成函式的參數傳遞和函式局部變數的儲存。 如果程式需要在執行的過程中動態分配內部記憶體,可以利用堆來實作。
基本上C程式的元素儲存在內部記憶體的時候有3種分配策略:
如果一個變數宣告為全域變數或者是函式的靜態變數,這個變數的儲存將使用靜態分配方式。靜態分配的內部記憶體一般會被編譯器放在資料段或代碼段來儲存,具體取決於實作。這樣做的前提是,在編譯時就必須確定變數的大小。 以 IA32 的 x86 平台及 gcc 編譯器為例,全域及靜態變數放在資料段的低端;全域及靜態常量放在代碼段的進階。
函式的自動局部變數應該隨著函式的返回會自動釋放(失效),這個要求在一般的體系中都是利用棧來滿足的。相比於靜態分配,這時候,就不必絕對要求這個變數在編譯時就必須確定變數的大小,執行時才決定也不遲,但是C89仍然要求在編譯時就要確定,而C99放鬆了這個限制。但無論是C89還是C99,都不允許一個已經分配的自動變數執行時改變大小。
所以說C函式永遠不應該返回一個局部變數的位址。
要指出的是,自動分配也屬於動態分配,甚至可以用alloca(3)函式來像分配堆一樣進行分配,而且釋放是自動的。
還有一種更加特殊的情況,變數的大小在執行時有可能改變,或者雖然單個變數大小不變,變數的數目卻有很大彈性,不能靜態分配或者自動分配,這時候可以使用堆來滿足要求。ANSI C 定義的堆操作函式是malloc(3)、calloc(3)、realloc(3)和free(3)。
使用堆內部記憶體將帶來額外的開銷和風險。
C語言的特色之一是:語言不負責內部記憶體邊界檢查。
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> | ASCIIZ字串及任意內部記憶體處理常式 |
| <time.h> | 時間相關 |
在95年的修正版中
在C99中增加了六個函式庫
以上是C語言的標準,而各個平台各自又對C函式庫函式進行的各種擴充,就浩如煙海了。如POSIX C、GNU C等。
| char | short | int | unsigned |
| long | float | double | struct |
| union | void | enum | signed |
| const | volatile | typedef | auto |
| register | static | extern | break |
| case | continue | default | do |
| else | for | goto | if |
| return | switch | while | sizeof |
| _Bool | _Complex | _Imaginary | inline | restrict |
C//是一種結構化的並列語言,讀作C parallel。它基於標準C語言但擴充套件了為數不多的構造用來表示並列性和行程互動。C//的核心是稱為一致區域的構造,它方便了結構化的、確定的、終止的和複合的並列程式的開發。
|
||||||||||||||||||||||||||||||||||||||||||
stock | retire | vm
Why are we here?
All text is available under the terms of the GNU Free Documentation License
This page is cache of Wikipedia. History