首先要明白的是,如今世界上很多大型的C語言程序項目,在幾十年前就開始了。

Unix 操作系統的開發始於 1969 年,在 1972 年 Unix 開發團隊使用C語言重寫了它的幾乎所有代碼。實際上,創建C語言的一個很大的原因就是為了將 Unix 內核代碼從程序集移動到更高級別的語言,這樣可以用更簡潔的代碼量行執行相同的任務。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

Oracle 數據庫的開發始於 1977 年,當時的開發語言是 assembly, 不過在 1983 年開發團隊使用 C語言重構了Oracle 數據庫的代碼,如今它已經成為世界上最流行的數據庫之一。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

1985年,Windows 1.0 發佈。雖然 Windows 源代碼不是公開的,但有人說它的內核大部分是用 C語言編寫的,小部分使用了彙編。Linux 內核開發始於 1991 年,也是用 C 語言編寫的,1992 年,它以 GNU 許可證發佈,並作為 GNU 操作系統的一部分使用。GNU 操作系統本身是使用 C語言 和 Lisp 編程語言開發的,它的許多組件都是用 C語言編寫的。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

但是C語言編程並不侷限於上面列舉的這些幾十年前就開始的項目,當時沒有今天那麼多的編程語言。事實上,許多C語言項目今天仍在不斷進行。

C 是驅動科技的基石

儘管現在其他高級語言(如 java、python)看起來似乎更加流行,但C語言仍在世界科技的發展中,具有不可磨滅的作用。下面這些產品或者服務都離不開C語言,幾乎每一個人每天都在使用。

微軟的 windows 操作系統

就像上面說的,微軟的 Windows 內核主要是用C語言實現的,只有極少部分是用匯編語言開發的。幾十年來,windows 操作系統是世界上使用最多的操作系統,約佔市場份額的 90%,要明白這離不開C語言編寫的內核提供支持。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

Linux

Linux 也主要是用C語言編寫的,其中小部分使用了彙編語言。世界上前 500 名最強大的超級計算機中,大約 97% 運行Linux內核。當然了,現在 Linux 也用於許多個人電腦。

蘋果的 Mac

蘋果的 Mac 電腦的操作系統 OS X 內核也是由 C語言編寫的。Mac 電腦中的每個程序和驅動程序,就和 Windows 和 Linux 電腦一樣,可以說都是由 C語言程序提供動力的。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

手機

iOS、Android 和 Windows Phone 內核也是用C語言編寫的。手機上常用的這幾個系統只是對現有 Mac OS、Linux 和Windows 內核做了一定的適應性移植,所以你每天使用的智能手機其實都是運行在C語言開發的內核上的。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

數據庫

現在世界上最流行的幾大數據庫,包括 Oracle 數據庫、MySQL、MS SQL Server 和 PostgreSQL,基本上都是使用 C語言開發的(前三個同時使用了 C語言和 C++)。數據庫廣泛用於各種系統:金融、政府、媒體、娛樂、電信、衛生、教育、零售、社會網絡、物聯網等。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

3D電影

3D電影通常是用 C語言和C++編寫的應用程序創建的。這些應用程序需要非常高的運行效率,因為有超多的數據需要處理。事實上,這些軟件的處理效率越高,藝術家和動畫師製作電影鏡頭所需的時間就越少,公司節省的錢也就越多。

嵌入式系統

想象有一天你醒來去上班,喚醒你的鬧鐘可能是用C語言編寫的,你用來存放食物的冰箱,你的空調、電視、收音機也是由嵌入式系統控制的。甚至你汽車的這些配件也離不開C語言編寫的嵌入式系統:

  • 自動變速器
  • 胎壓檢測系統
  • 傳感器(氧氣、溫度、油位等)
  • 座椅和後視鏡設置記憶
  • 儀表板顯示屏
  • 防抱死制動器
  • 自動穩定控制
  • 兒童防鎖
  • 加熱座椅
  • 氣囊控制

去商店,停車,然後去自動售貨機買汽水。廠家會用什麼語言來編程這個自動售貨機?可能是C語言。然後你在商店買東西,收銀機也是用C語言編程的。用掃碼支付寶微信支付時,掃碼機很可能也是用C語言編程的。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

凡是有些複雜的電子系統,幾乎都會用到嵌入式系統。它們就像小型計算機,裡面有一個微控制器/微處理器,在嵌入式設備上運行一個程序,也叫做固件。該程序檢測按鍵並相應地執行操作,還向用戶顯示信息。

例如,鬧鐘必須與用戶交互,檢測用戶按的是什麼按鈕,有時還需要感知用戶按了多長時間。因此C語言程序員要對設備進行編程,向用戶顯示相關信息。再比如,汽車的防抱死制動系統必須能夠檢測到輪胎的突然鎖定,並在短時間內釋放制動器,從而防止不受控制的打滑。這些計算都是由C語言編寫的嵌入式系統完成的。

雖然在嵌入式系統上使用的編程語言可能因品牌不同而有所不同,但由於C語言具有靈活性高、效率高、性能優秀,以及與硬件的聯繫緊密等特點,最常用的編程語言是C語言。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

為什麼仍然使用C編程語言?

如今和幾十年前不同,已經有許多其他編程語言允許開發人員在不同類型的項目中使用。在某些項目中,它們的開發效率確實往往比C語言更加高效。有一些更高級的語言提供了更大的內置庫,這些庫簡化了JSON、XML、UI、網頁、客戶機請求、數據庫連接、媒體操作等的工作。

但儘管如此,C語言在自己的領域還未逢對手。在底層領域,系統能夠提供的資源比較少,但是對效率的要求比較高的情況下,C語言幾乎是唯一的選擇。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

可移植性和效率

C語言是一種可移植的語言,它儘可能貼近機器,但是卻幾乎可以普遍用於現有的處理器體系結構——幾乎每個現有的體系結構都至少有一個C編譯器。要知道,現在由於現代編譯器生成了高度優化的二進制文件,用手工編寫的程序集來改進它們的輸出並不是一件容易的事情。

其他編程語言的編譯器、庫和解釋程序通常用C語言實現。像python、ruby 和 php 這樣的解釋語言的主要實現是用 C語言完成的,編譯器甚至使用它來與機器進行其他語言的通信。例如,C語言是 Eiffel 及 Forth 的中間語言。這意味著,這些語言的編譯器只生成C語言代碼,而不是為要支持的每個體系結構生成機器代碼,生成機器代碼的工作由C語言編譯器實現。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

C語言也成為了開發人員之間交流的通用語言。C是一種很好的語言,可以用大多數人都能接受的方式表達編程中的共同想法。相當一部分編程語言都能看到 C語言的影子,因此即使一些程序員不瞭解C,C語言程序員也能與他們交談。

內存操作

能夠進行任意內存地址訪問和指針算法,是C語言非常適合系統編程(操作系統和嵌入式系統)的一個重要原因。在硬件/軟件邊界,計算機系統和微控制器將其外圍設備和I/O管腳映射到內存地址。系統應用程序必須讀寫這些自定義內存,才能與外界通信。因此,C語言處理任意內存地址的能力對於系統編程是必不可少的。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言


例如,微控制器的結構可以使存儲器地址 0x40008000 中的字節在地址 0x40008001 的位 4 設置為 1 時由通用異步收發器(或 UART 串口通信,用於與外設通信的通用硬件組件)發送,並且在設置該位之後自動執行。這是通過該UART發送字節的C語言函數的代碼:

#define UART_BYTE *(char *)0x40008000 
#define UART_SEND *(volatile char *)0x40008001 |= 0x08
void send_uart(char byte)
{
UART_BYTE = byte; // write byte to 0x40008000 address
UART_SEND; // set bit number 4 of address 0x40008001
}

函數的第一行將擴展為:

*(char *)0x40008000 = byte;
C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

此行告訴編譯器將值 0x40008000 解釋為指向一個字符的指針,然後對該指針(使用最左邊的* 運算符)取消引用(給出指向的值),最後將字節值賦給該未引用的指針。換句話說:將變量 byte 的值寫入內存地址 0x40008000。

下一行將擴展為:

*(volatile char *)0x40008001 |= 0x08;

在這一行中,我們對地址 0x40008001 處的值和 0x08(二進制形式為000001000,即位號4中的1)執行按位或運算,並將結果保存回地址 0x40008001。換句話說:這行C語言代碼設置了地址為 0x40008001 的字節的第4位。程序還聲明地址 0x40008001 處的值是 volatile 的。這告訴編譯器這個值可能被代碼外部的進程修改,因此編譯器在寫入之後不會對該地址中的值做任何優化。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

資源的確定性使用

系統編程不能依賴的一個公共語言特性是垃圾收集機制。嵌入式應用程序的時間和內存資源非常有限,它們通常用於實時系統,在這些系統中無法提供對垃圾收集器的非確定性調用。如果由於內存不足而無法使用動態分配,那麼使用C語言指針允許的其他內存管理機制(如在自定義地址中放置數據)是非常重要的。嚴重依賴動態分配和垃圾收集的語言不適合資源有限的系統。

代碼大小

C語言程序的運行時間很短,而且其代碼的內存佔用比大多數其他語言都要小。

例如,與C++相比,進入嵌入式設備的C生成二進制文件大小大約是由類似C++代碼生成的二進制文件大小的一半。其中一個主要原因是C++多了異常支持。異常是C++在C上添加的一個很好的工具,如果沒有觸發和巧妙地實現,它們實際上沒有執行時間開銷(但是以增加代碼大小為代價)。

來看一個例子:

// Class A declaration. Methods defined somewhere else; 
class A
{
public:
A(); // Constructor
~A(); // Destructor (called when the object goes out of scope or is deleted)
void myMethod(); // Just a method
};
// Class B declaration. Methods defined somewhere else;
class B
{
public:
B(); // Constructor
~B(); // Destructor
void myMethod(); // Just a method
};
// Class C declaration. Methods defined somewhere else;
class C
{
public:
C(); // Constructor
~C(); // Destructor
void myMethod(); // Just a method
};
void myFunction()
{
A a; // Constructor a.A() called. (Checkpoint 1)
{
B b; // Constructor b.B() called. (Checkpoint 2)
b.myMethod(); // (Checkpoint 3)
} // b.~B() destructor called. (Checkpoint 4)
{
C c; // Constructor c.C() called. (Checkpoint 5)
c.myMethod(); // (Checkpoint 6)
} // c.~C() destructor called. (Checkpoint 7)
a.myMethod(); // (Checkpoint 8)
} // a.~A() destructor called. (Checkpoint 9)
C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

A、B 和 C 類的方法在其他地方定義(例如在其他文件中),編譯器不能分析它們,也不知道它們是否會拋出異常。因此,編譯器必須準備處理來自其任何構造函數、析構函數或其他方法調用的異常。

如果MyFunction中的任何調用引發異常,則堆棧展開機制必須能夠為已構造的對象調用所有析構函數。堆棧展開機制的一個實現將使用該函數最後一次調用的返回地址來驗證觸發異常的調用的“Checkpoint num”。它通過使用一個輔助的自動生成函數(一種查找表)來實現這一點,如果從函數體中拋出異常,該函數將用於堆棧展開,類似於:

// Possible autogenerated function
void autogeneratedStackUnwindingFor_myFunction(int checkpoint)
{
switch (checkpoint)
{
// case 1 and 9: do nothing;
case 3: b.~B(); goto destroyA; // jumps to location of destroyA label
case 6: c.~C(); // also goes to destroyA as that is the next line
destroyA: // label
case 2: case 4: case 5: case 7: case 8: a.~A();
}
}
C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

如果從 Checkpoint 1 和 Checkpoint 9 拋出異常,則不需要銷燬任何對象。對於 Checkpoint 3,必須銷燬 B 和 A。對於 Checkpoint 6,必須銷燬 C 和 A 。在任何情況下,都必須遵守銷燬令。對於 Checkpoint 2、4、5、7 和 8,只需要銷燬對象 A。

此輔助功能會增加代碼大小,這是 C++ 增加的空間開銷的一部分,許多嵌入式應用程序不能負擔這個額外的空間。因此,嵌入式系統的 C++ 編譯器常常有一個禁用異常的標誌。禁用 C++中 的異常不是免費的,因為標準模板庫很大程度上依賴於異常來通知錯誤。使用這種修改的方案,對C++程序開發人員的要求自然就比較高了。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

對於其他語言來說,二進制大小的增加會使其他額外的開銷變得更為糟糕,這些特性是非常有用但不能被嵌入式系統提供的。C語言沒有提供這些額外特性的使用,因此它能夠實現比其他編程語言更緊湊的代碼。

為什麼學習C語言

C語言不是一門難學的語言,如前所述,C語言是程序開發人員的通用語言。許多新算法在書中或互聯網上的實現首先(或僅)由作者以C語言提供。這提供了最大可能的可移植性。我見過程序員在互聯網上為把C語言算法改寫成其他編程語言而苦苦掙扎,因為他們不知道C語言的基本概念。

C語言是一種古老而廣泛的語言,因此可以在網上找到用C語言編寫的所有算法,從各位前輩的經驗中獲益匪淺。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言

當我與同事討論代碼的某些部分或其他語言的某些特性的行為時,我們最終會“用C語言交談”:這部分是向對象傳遞“指針”還是複製整個對象?這裡會有 cast 嗎?等等。

在分析高級語言的一部分代碼時,我們很少討論彙編指令。相反,當討論機器在做什麼時,用C語言基本上就能非常清楚地表達。許多有趣的項目,從大型數據庫服務器或操作系統內核,到小型嵌入式應用程序,都是能以C語言為出發點深入瞭解的。

小結

光明會不會統治世界不知道,但C語言程序員一定會。C語言似乎永遠不會過時,它與硬件非常接近,可移植性強,資源使用確定性強,是操作系統內核、嵌入式軟件等低層次開發的理想選擇。它的多功能性、高效性和良好的性能使它成為高複雜度數據處理軟件(如數據庫或3D動畫)的最佳選擇。

今天的許多編程語言在其預期用途上都優於C語言,但這並不意味著它們在所有領域都優於C語言。當性能是優先考慮的時候,C語言仍然是無與倫比的。

C語言廉頗老矣?java那麼好用,為什麼還要使用C這麼古老的語言


不管人們是否能夠意識到,全世界都在使用C語言驅動給的設備。C語言從過去到現在都是人類科技不可缺少的,而且,對於軟件開發的許多領域來說,C語言仍然是未來。

迎在評論區一起討論,質疑。文章都是手打原創,每天最淺顯的介紹C語言、linux等嵌入式開發,喜歡我的文章就關注一波吧,可以看到最新更新和之前的文章哦。

相關推薦

推薦中...