Like Share Discussion Bookmark Smile

J.J. Huang   2019-10-04   C   瀏覽次數:

C語言 - 第零章 | 開卷

終於決定開始進入C語言地獄⋯
看到人家說「C語言,從崩潰到放棄」
我想在怎麼說C語言還是人看得懂的語言,還不至於到學不來吧?!反正反匯編都在分析在學習(組合語言、機器碼),這應該不會難到哪⋯

讓我們開始一步一步踏入C語言的世界裡面⋯
後面大概的順序是:

  • C語言歷史

  • C語言特性

  • 說明低中高階語言

  • 瞭解IDE是什麼

  • 學會安裝IDE

  • 學會基本IDE操作(包含C語言練習)

  • C語言的基礎學習

C語言歷史

早期發展

C語言最早由丹尼斯·里奇(Dennis Ritchie)為了在PDP-11電腦上運行的Unix系統所設計出來的程式語言,第一次發展在1969年到1973年之間。

C源於BCPL語言,後者由馬丁·理察德(Martin Richards)於1967年左右設計實現。BCPL是一門”無類型”的程式語言:它僅能操作一種資料類型,即機器字(machine word)。1970年,肯·湯普遜為執行在PDP-7上的首個Unix系統設計了一個精簡版的BCPL,這個語言被稱為B語言,它也是無類型的。

Unix最早運行在PDP-7上,是以組合語言寫成。在PDP-11出現後,丹尼斯·里奇與肯·湯普遜著手將Unix移植到PDP-11上,無類型的語言在PDP-11上愈發顯得合適。PDP-11提供了多種不同規格大小的基本物件:一位元組長的字元,兩位元組長的整型數以及四位元組長的浮點數。B語言無法處理這些不同規格大小的物件,也沒有提供單獨的運算子去操作它們。

C語言最初嘗試通過向B語言中增加資料類型的想法來處理那些不同類型的資料。和大多數語言一樣,在C中,每個物件都有一個類型以及一個值;類型決定了可用於值的操作的含義,以及物件占用的儲存空間大小。

1973年,Unix作業系統的核心正式用C語言改寫,這是C語言第一次應用在作業系統的核心編寫上。

1975年C語言開始移植到其他機器上使用。史蒂芬·強生實現了一套「可移植編譯器」,這套編譯器修改起來相對容易,並且可以為不同的機器生成程式碼。從那時起,C在大多數電腦上被使用,從最小的微型電腦到與CRAY-2超級電腦。C語言很規範,即使沒有一份正式的標準,你也可以寫出C程式,這些程式無須修改就可以執行在任何支援C語言和最小執行時環境的電腦上。

C最初在小型機器上實現,並且繼承了一系列小語種程式語言的特點;與功能相比,C的設計者更傾向於簡單和優雅。此外,從一開始,C語言就是為系統級編程而設計,程式的執行效率至關重要,因此,C語言與真實機器能力的良好匹配也就不足為奇。例如,C語言為典型硬體所直接支援的物件:字元,整數(也許有多種大小),以及浮點數(同樣可能有多種大小)提供了相應的基本資料類型。

K&R C

1978年,丹尼斯·里奇和布萊恩·柯林漢合作出版了《C程式設計語言》的第一版。書中介紹的C語言標準也被C語言程式設計師稱作「K&R C」(柯里C),第二版的書中也包含了一些ANSI C的標準。

K&R C主要引入了以下語言特性:

  • 標準I/O庫
  • 結構(struct)類型
  • 長整數(long int)類型
  • 無號整數(unsigned int)類型
  • 把運算子=+和=-改為+=和-=。因為=+和=-會使得編譯器不知道使用者要處理i = -10還是i =- 10,使得處理上產生混淆。
  • 即使在後來ANSI C標準被提出的許多年後,K&R C仍然是許多編譯器的最低標準要求,許多老舊的編譯仍然運行K&R C的標準。

ANSI C 和 ISO C

1989年,C語言被美國國家標準協會(ANSI)標準化,編號為ANSI X3.159-1989。這個版本又稱為C89。標準化的一個目的是擴充K&R C,增加了一些新特性。

  • void 函式
  • 函式返回 struct 或 union 類型
  • void * 資料類型

1990年,國際標準化組織(ISO)成立 ISO/IEC JTC1/SC22/WG14 工作群組,來規定國際標準的C語言,通過對ANSI標準的少量修改,最終製定了 ISO 9899:1990,又稱為C90。隨後,ANSI亦接受國際標準C,並不再發展新的C標準。

K&R C語言到ANSI/ISO標準C語言的改進包括:

  • 增加了真正的標準庫
  • 新的預處理命令與特性
  • 函式原型允許在函式申明中指定參數類型
  • 一些新的關鍵字,包括 const、volatile 與 signed
  • 寬字元、寬字串與多位元組字元
  • 對約定規則、聲明和型別檢查的許多小改動與澄清

WG14工作小組之後又於1994年,對1985年頒布的標準做了兩處技術修訂(缺陷修復)和一個補充(擴充)。下面是 1994 年做出的所有修改:

  • 3 個新的標準庫標頭檔 iso646.h、wctype.h 和 wchar.h
  • 幾個新的記號與預定義巨集,用於對國際化提供更好的支援
  • printf/sprintf 函式一系列新的格式程式碼
  • 大量的函式和一些類型與常數,用於多位元組字元和寬位元組字元

C99

在ANSI的標準確立後,C語言的規範在一段時間內沒有大的變動,然而C++在自己的標準化建立過程中繼續發展壯大。《標準修正案一》在1994年為C語言建立了一個新標準,但是只修正了一些C89標準中的細節和增加更多更廣的國際字元集支援。不過,這個標準引出了1999年ISO 9899:1999的發表。它通常被稱為C99。C99被ANSI於2000年3月採用。

在C99中包括的特性有:

  • 增加了對編譯器的限制,比如源始碼每行要求至少支援到 4095 位元組,變數名函式名的要求支援到 63 位元組(extern 要求支援到 31)。
  • 增強了預處理功能。例如:
    • 巨集支援取可變參數 #define Macro(…) __VA_ARGS__
    • 使用巨集的時候,允許省略參數,被省略的參數會被擴充成空串。
    • 支援 // 開頭的單行注釋(這個特性實際上在C89的很多編譯器上已經被支援了)
  • 增加了新關鍵字 restrict, inline, _Complex, _Imaginary, _Bool
    • 支援 long long, long double _Complex, float _Complex 等類型
  • 支援不定長的陣列,即陣列長度可以在執行時決定,比如利用變數作為陣列長度。聲明時使用 int a[var] 的形式。不過考慮到效率和實現,不定長陣列不能用在全域,或 struct 與 union 。
  • 變數聲明不必放在語句塊的開頭,for 語句提倡寫成 for(int i=0;i<100;++i) 的形式,即i 只在 for 語句塊內部有效。
  • 允許採用(type_name){xx,xx,xx} 類似於 C++ 的建構函式的形式構造匿名的結構體。
  • 初始化結構的時候允許對特定的元素賦值,形式為:
1
struct test{int a[3],b;} foo[] =  { [0].a = {1}, [1].a = 2 };
1
struct test{int a, b, c, d;} foo =  { .a = 1, .c = 3, 4, .b = 5 };  // 3,4 是對 .c,.d 賦值的
  • 格式化字串中,利用 \u 支援 unicode 的字元。
  • 支援 16 進位的浮點數的描述。
  • printf scanf 的格式化串增加了對 long long int 類型的支援。
  • 浮點數的內部資料描述支援了新標準,可以使用 #pragma 編譯器指令指定。
  • 除了已有的 __line__ __file__ 以外,增加了 __func__ 得到目前的函式名。
  • 允許編譯器化簡非常數的運算式。
  • 修改了 / % 處理負數時的定義,這樣可以給出明確的結果,例如在C89中-22 / 7 = -3, -22 % 7 = -1,也可以-22 / 7= -4, -22 % 7 = 6。 而C99中明確為 -22 / 7 = -3, -22 % 7 = -1,只有一種結果。
  • 取消了函式返回類型預設為 int 的規定。
  • 允許在 struct 的最後定義的陣列不指定其長度,寫做 [](flexible array member)。
  • const const int i 將被當作 const int i 處理。
  • 增加和修改了一些標準標頭檔,比如定義 bool 的 <stdbool.h> ,定義一些標準長度的 int 的 <inttypes.h> ,定義複數的 <complex.h> ,定義寬字元的 <wctype.h> ,類似於泛型的數學函式 <tgmath.h>, 浮點數相關的 <fenv.h>。 在<stdarg.h> 增加了 va_copy 用於複製 … 的參數。<time.h> 里增加了 struct tmx ,對 struct tm 做了擴充。
  • 輸入輸出對寬字元以及長整數等做了相應的支援。

但是各個公司對C99的支援所表現出來的興趣不同。當GCC和其它一些商業編譯器支援C99的大部分特性的時候,微軟和Borland卻似乎對此不感興趣。

C11

2011年12月8日,ISO正式發布了新的C語言的新標準C11,之前被稱為C1X,官方名稱為ISO/IEC 9899:2011。新的標準提高了對C++的相容性,並增加了一些新的特性。這些新特性包括泛型巨集、多執行緒、帶邊界檢查的函式、匿名結構等。

C18

C18沒有引入新的語言特性,只對C11進行了補充和修正。


C語言特性

它的特性,總歸一句話「就是沒有特性」

由於很接近底層的組合語言,所以能做很多事情,透過指標,能夠直接控制、管理記憶體。
C語言的設計理念是把工程師視為「成人」、「工程師」應該知道自己該做什麼,
不像「小孩」需要特別的保護,他盡量給予工程師最大的彈性、自由,所以若是對C語言了解不夠深,就容易寫出有漏洞的程式,成為駭客攻擊的目標。

簡單的說,基本上,C語言「想做什麼,就做什麼,做錯什麼,自己負責」。


低中高階語言

程式語言就是人與電腦溝通的橋樑,就像世界上有很多語言,像是中文、英文、日文等等,
程式語言也有很多種,像是C、C++、Java、Python,使用程式語言寫程式即可電腦溝通。

CPU只看得懂一種語言,叫做機器碼(Machine code),
所以需要跟CPU溝通的話就需要翻譯的存在, 將程式碼轉換成機器碼有三種方式,組譯、編譯、直譯

  • 組譯(Assembling)
    • 將組合語言轉換成機器語言
    • 反過來將機器語言變成組合語言(Disassemble)
      • 玩資安,寫外掛的重要技能
    • ex. 組合語言使用組譯器
  • 直譯
    • 透過直譯器(interpreter)對原始碼進行一邊讀解,一邊執行的動作
    • 優點,在每個指令打完後就可以知道結果,較適合新手(but直譯跳到編譯難)
    • 每次執行每次直譯
    • ex. python使用直譯器
  • 編譯
    • 將原始程式碼透過編譯器(Compiler)轉換成機器碼,再直接執行機器碼
    • 優點;轉換速度快,可以一次找出不合文法的內容
    • 原始程式 –> 經由編譯器 –> 變成目的檔 –> 經由連結器 –> 變成執行檔
    • SOURCE.C –> COMPILER –> SOURCE.OBJ –> LINKER –> SOURCE.EXE
    • 編譯過一次後,以後只需要執行機器碼
    • ex. C語言使用編譯器

越接近CPU語言的叫低階語言,
越接近人類語言的較高階語言,
還有個剛好在中間的叫做中階語言。

  • 低階語言

    • 每一道指令對應一個 CPU 控制碼、程式碼超級多、重複性大
    • 對硬體控制較好
    • 程式效能較高
    • 比較難懂
    • ex. 組合語言、機器碼
  • 中階語言

    • 原本C也算是高階語言,不過有些高階語言使用C編譯完的函式庫(Library)
    • 因此有些人也稱C為高階語言
    • ex. C語言
  • 高階語言

    • 比較好懂,程式碼較少
    • 較容易撰寫、除錯
    • 對硬體控制能力較差、程式效能也較差
    • ex. python、Java

同一件事情,假設高階語言要寫十行程式碼,
低階語言可能需要五百行,
但,低階語言能直接做到很多高階語言不容易做到的事情。


註:以上參考了
wiki C語言
hackersir gitbooks