文章目錄
1. C 語言起源
1972 年,貝爾實驗室的 丹尼斯·裏奇(Dennis Ritch)和 肯·湯姆遜(Ken Thompson)在研究 Unix 操作系統時設計了 C 語言。C 語言是在 B 語言(湯姆遜發明)的基礎上進行設計的。
2. C 語言標準
2.1 K&R C 標準
1978 年,布萊恩·柯林漢(Brain Kernighan)和丹尼斯·裏奇(Dennis Ritch)合著的 The C Programming Language (《C 語言程序設計》)第一版是公認的 C 標準,通常稱之爲 K&R C 或經典 C。
2.2 ANSI/ISO C 標準
美國國家標準協會(ANSI)於 1983 年組建一個委員會開發了一套新標準,並於 1989 年正式公佈。該標準(ANSI C)定義了 C 語言和 C 標準庫。國際標準化組織(ISO)於 1990 年採用了這套 C 標準(ISO C)。所以兩者是完全相同的標準。ANSI/ISO 標準通常也叫作 C89 或 C90。
2.3 C99 標準
1994 年,ANSI/ISO 聯合委員會開始修訂 C 標準,最終在 1999 年發佈了 C99 標準。(並不是所有的編譯器都支持 C99)
2.4 C11 標準
2011 年標準委員會發布了 C11 標準。(C99 的一些特性成爲 C11 的可選項)
3. 編程機制
用 C 語言編寫程序時,編寫的內容被存儲在文本文件中,該文件被稱爲源代碼文件(source code file),該文件以 .c 結尾(如:hello.c)。在文件名中,點號(.)前面的部分稱爲基本名(basename),點號後面的部分稱爲擴展名(extension)。
3.1 目標代碼文件、可執行文件和庫
C 語言編程的基本策略是,用程序將源代碼文件轉換成可執行文件。典型的 C 實現通過編譯和鏈接兩個步驟來完成這一過程。編譯器將源代碼轉換成中間代碼,鏈接器將中間代碼和其他代碼合併,生成可執行文件。
中間文件有很多種形式,其中最普遍的是把源代碼轉換成機器語言代碼,並把結果放在目標代碼文件中(簡稱目標文件或目標代碼)。雖然目標文件中含有機器語言代碼,但是並不能直接運行該文件,因爲首先目標文件中缺少啓動代碼(startup code),啓動代碼充當着程序和操作系統之間的接口;其次,目標文件還缺少庫函數,幾乎所有的 C 程序都要使用 C 標準庫中的函數。
鏈接器在作用是,把目標代碼、系統的標準啓動代碼和庫代碼這 3 部分合併成一個文件,即可執行文件。對於庫代碼,鏈接器只會把程序中要用到的庫函數代碼提取出來。
3.2 Linux 系統
Linux 系統中要編寫運行 C 程序,需要使用 GNU 提供的 GCC 公用域 C 編譯器。如果 Linux 之前沒有安裝 GCC,則需要輸入以下命令安裝GCC。
yum install gcc
gcc 指令基本語法如下:
gcc -o 可執行文件名 源代碼文件
例子:在 Linux 系統中編寫一個 C 程序。
- 創建 hello.c 文件
vim hello.c
- 在 hello.c 中編寫源代碼並保存
#include <stdio.h>
int main(void)
{
printf("hello world!\n");
return 0;
}
- 編譯
gcc -o hello.out hello.c
- 運行可執行文件
./hello.out
4. C 程序示例
#include <stdio.h>
void hello(void); /*ANSI/ISO C 函數原型*/
int main(void)
{
int num; /*聲明(定義)一個名爲num的變量*/
num = 1; /*爲num賦一個值1*/
printf("hello world!\n"); // 使用printf函數,打印“hello world!”
hello();
return 0;
}
void hello(void) // 函數定義的開始
{
printf("hello C!");
}
4.1 #include 指令和頭文件
#include 這行代碼是一條 C 預處理器指令。通常,C 編譯器在編譯前會對源代碼做一些準備工作,即預處理。所有的 C 編譯器都提供 stdio.h 文件。該文件名的含義是標準輸入/輸出頭文件。該文件包含了供編譯器使用的輸入和輸出函數(printf 函數)信息。通常,在 C 程序頂部的信息集合被稱爲 頭文件(header)。
頭文件包含了編譯器創建最終可執行程序要用到的信息。例如,頭文件中可以定義一些常量,或者指明函數名以及如何使用它們。但是,函數的實際代碼在一個預編譯代碼的庫文件中。
4.2 main 函數
C 程序一定從 main() 函數開始執行(目前不需要考慮額外情況)。除了 main() 函數,我們還可以任意命名其他函數,但是 main() 函數必須是開始函數。圓括號用於識別這是一個函數。
int 是 main() 函數的返回類型,代表返回的值是整數。這裏的返回值返回給了操作系統。
通常,函數名後面的圓括號中包含了一些傳入函數的信息。該例中沒有傳遞任何信息,因此圓括號內是單詞 void。
如果瀏覽舊式的 C 代碼,可能會發現程序以如下形式開始:main()。C90 勉強接受這種形式,但是 C99 和 C11 標準不允許這樣寫。此外,我們還可以看到這樣的形式:void main()。一些編譯器允許這樣寫,但是所有的標準中都未認可這種寫法。所以這種寫法不推薦使用。
4.3 註釋
在程序中,被 /* */ 兩個符號括起來的部分是程序的註釋。較長的註釋可單獨放一行或者多行,在 /* 和 */ 之間的內容都會被編譯器忽略。C99 新增了另一種風格的註釋,普遍用於 C++ 和 Java。這種風格使用 // 符號創建註釋,但僅限於單行。
4.4 花括號、函數體和塊
在上面的程序中,花括號 { } 把 main 函數括起來。一般而言,所有的 C 函數都使用花括號標記函數體的開始和結束。這是規定,不能省略。花括號還可以把函數中的多條語句合併爲一個單元或塊。
4.5 聲明
聲明是 C 語言最重要的特性之一。在該例中,聲明完成了兩件事,第一,在程序中有一個名爲 num 的變量(variable)。第二,int 表名 num 是一個整數。int 是一種數據類型。編譯器使用這些信息爲 num 變量在內存中分配存儲空間。
int 是 C語言的一個 關鍵字,表示一種基本的 C 語言數據類型。關鍵字是語言定義的單詞,不能做其他用途,比如,不能用關鍵字作爲函數名或者變量名。示例中 num 是一個 標誌符(identifier),也就是一個變量、函數或者其他實體的名稱。因此, 聲明把特定標誌符與計算機內存中的特定位置聯繫起來,同時也確定了儲存在某位置的信息類型或者數據類型。
標誌符的命名規則:由字母、數字和下劃線構成,數字不能作爲第一個字符。
在 C 語言中,所有的變量必須先聲明才能使用。這意味着必須列出程序中用到的所有變量名及其類型。以前的 C 語言,還要求把變量的聲明放在塊的頂部,其他語句不能在任何聲明前面。但是 C99 和 C11 遵循 C++ 的慣例,可以把聲明放在塊中的任何位置。儘管如此,首次使用變量之前一定要先聲明它。
當一行中聲明兩個或多個變量時,變量之間用逗號分隔。
hello() 函數在程序中出現了三次,第 1 次是函數原型(prototype),告知編譯器在程序中要使用該函數;第 2 次是以函數調用的形式出現在 main() 函數中;最後一次出現在函數定義中,函數定義即函數本身的源代碼。
C90 標準新增了函數原型,函數原型是一種聲明形式,告知編譯器正在使用某函數,因此函數聲明也被稱作函數聲明。函數原型還指明瞭函數的屬性,比如函數的返回值類型,參數。C 標準建議,要爲程序中所有的函數提供函數原型。早期的 C 語言支持一種更簡單的函數聲明,只需指定返回類型,不用描述參數。C90、C99 和 C11標準都承認舊版本的形式,但是也表明會逐漸淘汰這種過時的寫法。
4.6 賦值
賦值是 C 語言的基本操作之一。num = 1;的意思是 “把值 1 賦給變量 num”。在聲明變量時,編譯器在計算機內存中爲變量 num 預留了空間,然後執行賦值語句時,把值存儲在之前的預留位置。可以個給 num 賦不同的值,這就是 num 被稱爲變量的原因。
賦值表達式語句從右側把值賦到左側。
4.7 printf() 函數
printf() 函數是 C 語言的一個標準函數。圓括號中內容是從 main() 函數傳遞給 printf() 函數的信息,該信息稱爲參數,更確切的說是函數的實際參數(actual argument)。在 C 語言中,實際參數(簡稱實參)是傳遞給函數的特定值,形式參數(簡稱形參) 是函數中用於存儲值的變量。
printf() 函數會查看其雙引號中的內容,並將其打印在屏幕上。
printf() 函數的雙引號中的 \n 代表是一個換行符。換行符是一個 轉義序列(escape sequence)。轉義序列用於代表難以表示或無法輸入的字符。每個轉義序列都以反斜槓字符(\)開始。
4.8 return 語句
int main(void) 中 int 表明 main 函數應該返回一個整數。有返回值的 C 函數要有 return 語句,該語句以 return 關鍵字開始,後面是待返回的值,並以分號結尾。如果遺漏 main() 函數中的 return 語句,程序在運行至最外面的右花括號( })時會返回 0。因此可以省略 main() 函數末尾的 return 語句。但是不要在其他有返回值的函數中遺漏它。
5. 關鍵字和保留標識符
ISO/ANSI C 標準 32 個關鍵字:
auto | break | case | char | const | continue | default | do |
---|---|---|---|---|---|---|---|
double | else | enum | extern | float | for | goto | if |
int | long | register | return | short | signed | sizeof | static |
struct | switch | typeof | union | unsigned | void | volatile | while |
C99 標準新增 5 個關鍵字:
inline | restrict | _Bool | _Complex | _Imaginary |
---|
C11 標準新增 7 個關鍵字:
_Alignas | _Alignof | _Atomic | _Static_assert | _Noreturn | _Thread_local | _Generic |
---|
關鍵字是 C 語言的詞彙,不能用它們作爲標誌符。如果關鍵字使用不當,編譯器會視爲語法錯誤。還有一些保留標誌符,C 語言已經指定了它們的用途或保留了它們的使用權,也不能作爲標識符。保留標識符包括那些以下劃線字符開頭的標識符和標準庫函數名,如 printf()。
6. 基本數據類型
6.1 基本類型關鍵字
最初 K&C 的關鍵字 | C90 標準添加的關鍵字 | C 90 標準添加的關鍵字 |
---|---|---|
int | signed | _Bool |
long | void | _Complex |
short | _Imaginary | |
unsigned | ||
char | ||
float | ||
double |
其中,int 關鍵字表示基本的整數類型,long、short、signed 和 unsigned 用於提供基本整數類型的變式。char 關鍵字用於指定字母和其它字符,也可以表示較小的整數。float、double 和 long double 代表浮點數類型。_Bool 代表布爾類型(true 或 false,1 表示 true,0 表示 false)。_Complex 代表複數,_Imaginary 代表虛數。
6.2 整數類型
有符號整型:
- int
- char
- signed char
- short 或 short int
- long 或 long int
- long long 或 long long int
無符號整型:
- unsigned int
- unsigned char
- unsigned short
- unsigned long
6.2 浮點類型
- float (單精度)
- double (雙精度)
- long double
6.3 類型大小
sizeof 是 C 語言的內置運算符,以字節爲單位指定類型的大小,C99 和 C1 提供 %zd 轉換說明匹配 sizeof 的返回類型。一些不支持 C99 和 C11 的編譯器可以用 %u 或 %lu 代替 %zd。
#include <stdio.h>
int main(void)
{
/* C99爲類型大小提供%zd轉換說明 */
printf("Type int has a size of %zd bytes.\n", sizeof(int));
printf("Type char has a size of %zd bytes.\n", sizeof(char));
printf("Type long has a size of %zd bytes.\n", sizeof(long));
printf("Type long long has a size of %zd bytes.\n", sizeof(long long));
printf("Type double has a size of %zd bytes.\n", sizeof(double));
printf("Type long double has a size of %zd bytes.\n", sizeof(long double));
return 0;
}
注意:C 語言中,各種類型的存儲大小與系統的位數有關,但目前通用以 64 位爲主。以下列出了 32 位系統和 64 位系統的存儲大小差別(windows 相同) :