C編程語言
C語言是一種高級電腦語言,它主要用來進行電腦的程式設計。C語言具有高效、靈活、功能豐富、表達力強和移植性好等的特點,在電腦語言中備受青睞。
C語言是由UNIX的研製者Ken Thompson於1970年研製出的B語言的基礎上發展和完善起來的。C語言可以廣泛應用於不同的作業系統,例如UNIX,MS-DOS、Windows、Linux等。C語言是一種面向過程的語言,同時具有高階語言和組合語言的優點。在C語言的基礎上發展起來的有面向物件的語言C++,網路上廣泛使用的Java、 JavaScript和C#等。
1983年,美國國家標準局語言標準化委員會對C語言進行了標準化,於1983年頒佈了第一個C語言標準草案(83 ANSI C),後來於 1987年又頒佈了另一個C語言標準草案(87 ANSI C)。
C語言的特色
C語言是高級程式語言,也就是說程式師不必知道具體的CPU型號也可以爲電腦進行程式編制。在程式能夠運行前,源代碼必須有編譯器編譯成機器語言。相對於組合語言只能針對具體型號的CPU才能運行,C語言的便捷性是很明顯的。
C語言的主要特性
- C語言保留了低階語言的特性,例如涉及記憶體的指標。
- C語言通過參數在函數裏傳遞數值。
- 使用了預處理機制,使得程式裏可以通過包含例如巨集處理的方式來處理根源程式。
- C語言提供了一套標準庫,這些庫裏提供了十分有用的功能。
但是並不是所有的這些特性都是有效的。例如,預處理通常作爲一個獨立的程式被處理,這使得與處理的程式並不一定被完全編譯。
雖然C是高階語言,但是它同時擁有一些組合語言的特性,對其他的語言來說這是接近低階語言的特點。例如,在C語言裏,程式師可以對電腦記憶體進行管理。在默認的情況下,C語言不會對陣列的範圍進行檢查,也就是說即使陣列越界,C語言也不會作出錯誤提示。對電腦記憶體的管理使得程式師可以變出更快捷、更有效的程式,這對於設備驅動程式來說尤爲重要。但是這也使得程式容易産生令人討厭的“臭蟲”,例如緩衝器溢出錯誤。然而,這些錯誤可以由一些工具來避免。
C語言的不足可以由由C語言發展而來的更新的編程語言改進。Cyclone語言的擁有提防對於記憶體錯誤的特性。C++和Objective C提供了用於面向物件的編程結構。Java和C#增加了面向物件的結構使得對記憶體的管理自動化。
C語言的歷史
C語言的第一次發展在1969年到1973年之間。C之所以被稱爲C是因爲C語言的很多特性是由一種更早的被稱爲B語言的編程語言中發展而來的。
到了1973年,C語言已經可以用來編寫Unix作業系統的內核。這是第一次用高階語言來編寫作業系統的內核。Ritchie和Brian Kernighan在1978年出版了C編程語言(也就是白皮書或K&R),這本書在很多年裏都被認爲是這種語言的規範。甚至在今天,這本著作仍然是一本不錯的指南。
1980年以後,貝爾實驗室使得C變得更爲廣泛的流行,使得C一度成爲了作業系統和應用程式編程的首選。甚至到今天,它仍被廣泛用於編寫作業系統以及作爲最廣泛的電腦編程教育的首選語言。
二十世紀八十年代晚期,Bjarne Stroustrup和貝爾實驗室爲C語言添加了面向物件的特性。他們把這種語言成爲了C++。C++現在廣泛應用的在Microsoft Windows下運行的商業應用程式的編制,然而C仍然是Unix世界的熱門編程語言。
C語言的版本
K&R C
C不斷的從它的第一版本進行改進。在1978年,Kernighan和Ritchie的C編程語言第一版出版。它介紹了下面的有關C語言版本的特性:
- struct資料類型
- long int資料類型
- unsigned int資料類型
- 把運算符=+改爲+=,依次類推。因爲=+使得編譯器混淆。
在以後的幾年裏,C編程語言一直被廣泛作爲C語言事實上的規範。在這本書中,C語言通常被表述成"K&R C"。(第二版的包括了ANSI C標準)
K&R C通常被作爲C編譯器所支援的最基本的C語言部分。雖然現在的編譯器並不一定都完全遵循ANSI標準,但K&R C作爲C語言的最底要求仍然要編程人員掌握。但是無論怎樣,現在使用廣泛的C語言版本都已經與K&R C相距甚遠了,因爲這些編譯器都使用ANSI C標準。
ANSI C和ISO C
1989年,C語言被ANSI標準化。(ANSI X3.159-1989)。標準化的一個目的是擴展K&R C。這個標準包括了一些新的特性。在K&R出版後,一些新的特徵被“非官方”的加到C語言中。
- void函數
- 函數返回struct或union類型
- void *資料類型
在ANSI標準化自己的過程中,一些新的特徵被加了進去。ANSI也標準了函數庫。ANSI C標準被ISO(國際標準化組織)採納成爲ISO 9899。ISO的第一個版本文件在1990年出版。
C99
在ANSI標準化後,C語言的標準在一段相當的時間內都保持不變,儘管C++繼續在改進。(實際上,Normative Amendment1在1995年已經開發了一個新的C語言版本。但是這個版本很少爲人所知。)標準在90年代才經歷了改進,這就是ISO9899:1999(1999年出版)。這個版本就是通常提及的C99。它被ANSI於2000年三月採用。
在C99中包括的特性有:
- 可變範圍的陣列
- 新增加的資料類型,包括long long int,布林類型和用於表示複數的類型
- 支援用//表示注釋(這個特性實際上在C89的很多編譯器上已經被支援了)
- snprintf
但是各個公司對C99的支援所表現出來的興趣不同。當GCC和其他一些商業編譯器支援C99的大部分特性的時候,微軟和Borland卻似乎對此不感興趣。
用C語言寫一個Hello World程式
下面是一個在標準外設上輸出Hello world得簡單程式,這種程式通常作爲開始學習編程語言時的第一個程式:
#include <stdio.h> int main(void) { printf("Hello, world!\n"); return 0; }
進一步瞭解C
一個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處執行。
switch通常用於對幾種有明確值的條件進行控制。它要求的條件值通常是整數或字元。與switch搭配的條件轉移是case。使用case後面的標值,控制程式將跳到滿足條件的case處一直往下執行,直到語句結束或遇到break。通常可以使用default把其他例外的情況包含進去。如果switch語句中的條件不成立,控制程式將跳到default處執行。switch是可以嵌套的。
switch (<運算式>) { case <值1> : <語句> case <值2> : <語句> default : <語句> }
迴圈語句
C語言有三種形式的迴圈語句:
do <語句> while (<運算式>);
while (<運算式>) <語句>;
for (<運算式1> ; <運算式2> ; <運算式3>) <語句>;
在while和do中,語句將執行到運算式的值爲零時結束。在do...while語句中,循環體將至少被執行一次。這三種迴圈結構可以互相轉化:
for (e1; e2; e3) s;
相當於
e1; while (e2) { s; e3; }
當迴圈條件一直爲真時,將産生閉環。
跳轉語句
跳轉語句包括四種:goto,continue,break和return。
goto語句是無條件轉移語句:
goto 標記
標記必須在當前函數中定義,使用“標記:”的格式定義。程式將跳到標記處繼續執行。由於goto容易産生閱讀上的困難,所以應該儘量少用。
continue語句用在迴圈語句中,作用是結束當前一輪的迴圈,馬上開始下一輪迴圈。
break語句用在迴圈語句或switch中,作用是結束當前迴圈,跳到循環體外繼續執行。但是使用break只能跳出一層迴圈。在要跳出多重迴圈時,可以使用goto使得程式更爲簡潔。
當一個函數執行結束後要返回一個值時,使用return。return可以跟一個運算式或變數。如果return後面沒有值,將執行不返回值。
在C99中運算符號
() [] -> . ! ++ -- (cast) | 括弧、成員、邏輯非、自加、自減、強制轉換 |
++ -- * & ~ ! + - sizeof | 單目運算符 |
* / % | 算術運算符 |
+ - | 算術運算符 |
<< >> | 位運算符 |
< <= > >= | 關係運算符 |
== != | 關係運算符號 |
& | 位與 |
^ | 位異或 |
| | 位或 |
&& | 邏輯與 |
|| | 邏輯或 |
?: | 條件運算符 |
= += -= *= /= %= <<= >>= &= |= ^= | 賦值運算符 |
, | 順序運算符 |
資料類型
基礎資料類型
注意:以下是典型的資料位元長和範圍。但是編譯器可能使用不同的資料位元長和範圍。這取決於使用的編譯器。請參考具體的參考手冊。
在頭文件<limits.h>和<float.h>中說明了基礎資料的長度。float,double和long double的範圍就是在IEEE 754標準中提及的典型資料。
關鍵字 | 位長 | 範圍 |
char |
1 | -128..127 or 0..255 |
unsigned char |
1 | 0..255 |
signed char |
1 | -128..127 |
int |
2 or 4 | -32768..32767 or -2147483648..2147483647 |
short int |
2 | -32768..32767 |
long int |
4 | -2147483648..2147483647 |
float |
4 | ??? |
double |
8 | ??? |
long double |
8 | ??? |
陣列
如果一個變數名後面跟著一個有數位的中括弧,這個聲明就是陣列聲明。字串也是一種陣列。它們以ASCII的NUL作爲陣列的結束。
例如:
- int myvector [100];
- char mystring [80];
- float mymatrix [3] [2] = {2.0 , 10.0, 20.0, 123.0, 1.0, 1.0}
- char lexicon [10000] [300] ; /* 共一千個最大長度爲300的字元陣列。*/
- int a[3][4];
上面最後一個例子創建了一個陣列,但也可以把它看成是一個多維陣列。注意陣列的下標從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] |
指標
如果一個變數聲明時在前面使用*號,表明這個變數是一個指標。
例如:
- int *pi; /* 指向整型資料的指標 */
- int *api[3]; /* 指向整型資料的一個三維陣列指標 */
- char **argv; /* 指向一個字元指標的指標 */
儲存在指標中的地址所指向的數值在程式中可以由*讀取。例如,在第一個例子中,*pi是一個整型資料。這叫做引用一個指標。
另一個運算符&,叫做取位址運算符,它將返回一個變數、陣列或函數的存儲位址。因此,下面的例子:
- int i, *pi; /* int and pointer to int */
- pi = &i;
i和*p在程式中可以相互交替使用,直到pi被改變成指向另一個變數的指標。
字串
要使用字串並不需要引用庫,但是C標準庫確實包含了一些用於對字串進行操作的函數,使得它們看起來就像字串而不是陣列。
- strcat(dest, source) - 連接兩個字串,把source加到dest末尾。
- strchr(s, c) -在字串c中找出字元s第一次出現的位置。當沒有找到時,返回Null。
- strcmp(a, b) - 比較字串a和b的大小。如果兩個字串相同,返回0;如果a>b,返回正數;如果a<b,返回負數。
- strcpy(dest, source) - 把字串source拷貝到字串dest中。
- strlen(st) - 返回字串st的長度。
- strncat(dest, source, n) - 把source中的n個字元追加到dest後面。null後面的值將不會被添加。
- strncmp(a, b, n) - 比較字串a和b中n個字元的大小。如果兩個字串相同,返回0;如果a>b,返回正數;如果a<b,返回負數。
- strncpy(dest, source, n) - 把字串source拷貝到字串dest中。(最多拷貝n個)
- strrchr(s, c) - 在s中查找最後一次出現c的位置。返回這個位置。如果找不到,返回null。
文件輸入/輸出=
在C語言中,輸入和輸出是經由標準庫中的一組函數來實現的。在ANSI/ISO C中,這些函數被定義在頭文件<stdio.h>中。
標準輸入/輸出
有三個標準輸入/輸出是預定義的:
- stdin 標準輸入
- stdout 標準輸出
- stderr 輸入輸出錯誤
這些定義在運行過程中是自動的打開和關閉的,所以它們並不需要進行顯式定義。
下面的這個例子顯示了一個過濾程式(filter program)是怎樣構成的。
- include <stdio.h>
int main() { int c;
while (( c = getchar()) != EOF ) { /* 對字元進行各種處理 */
if (anErrorOccurs) { fputs("an error eee occurred\n", stderr); break; }
/* ... */ putchar(c); /* ... */
} }
傳遞命令行參數
在命令行賦予程式的參數將通過兩個預定義的變數argc(存儲命令行參數的個數)以及一個字元型指標argv進行傳遞。所以命令:
myFilt p1 p2 p3
將使得程式獲得如下參數:
(注意:不能保證字串的存儲是連續的。)
對單個參數的讀取可以通過argv[1],argv[2],argv[3]。
C語言的標準庫
以下列出由C語言提供的標準函數庫,函數庫通過#include進行引用。
在C89標準中:
- <assert.h>
- <ctype.h>
- <errno.h>
- <float.h>
- <limits.h>
- <locale.h>
- <math.h>
- <setjmp.h>
- <signal.h>
- <stdarg.h>
- <stddef.h>
- <stdio.h>
- <stdlib.h>
- <string.h>
- <time.h>
在95年的修正版中
- <iso646.h>
- <wchar.h>
- <wctype.h>
在C99中增加了六個函數庫
- <complex.h>
- <fenv.h>
- <inttypes.h>
- <stdbool.h>
- <stdint.h>
- <tgmath.h>
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 |