C語言和C+的區別和聯絡

C語言和C++到底是什麼關係?

首先C++和C語言本來就是兩種不同的程式語言,但C++確實是對C語言的擴充和延伸,並且對C語言提供後向相容的能力。對於有些人說的C++完全就包含了C語言的說法也並沒有錯。

C語言和C+的區別和聯絡

C++一開始被本賈尼·斯特勞斯特盧普(Bjarne Stroustrup)發明時,起初被稱為“C with Classes”,即「帶類的C」。

很明顯它是在C語言的基礎上擴充了類class等面向物件的特性和機制。但是後來經過一步步修訂和很多次演變,最終才形成了現如今這個支援一系列重大特性的龐大程式語言。

C語言和C+的區別和聯絡

1、C語言是面向過程語言,而C++是面嚮物件語言

我們都知道C語言是面向過程語言,而C++是面嚮物件語言,說C和C++的區別,也就是在比較面向過程和麵向物件的區別。

(1)面向過程和麵向物件的區別

面向過程:面向過程程式設計就是分析出解決問題的步驟,然後把這些步驟一步一步的實現,使用的時候一個一個的依次呼叫就可以了。

面向物件:面向物件程式設計就是把問題分解成各個物件,建立物件的目的不是為了完成一個步驟,而是為了描述某個事物在整個解決問題的步驟中的行為。

C語言和C+的區別和聯絡

(2)面向過程和麵向物件的優缺點

面向過程語言

優點:效能比面向物件高,因為類呼叫時需要例項化,開銷比較大,比較消耗資源;比如微控制器、嵌入式開發、 Linux/Unix等一般採用面向過程開發,效能是最重要的因素。

缺點:沒有面向物件易維護、易複用、易擴充套件

面嚮物件語言

優點:易維護、易複用、易擴充套件,由於面向物件有封裝、繼承、多型性的特性,可以設計出低耦合的系統,使系統更加靈活、更加易於維護

缺點:效能比面向過程低。

C語言和C+的區別和聯絡

二、具體語言上的區別

1、關鍵字的不同

C語言有32個關鍵字;

C++有63個關鍵字;

2、字尾名不同

C原始檔字尾。c,C++原始檔字尾。cpp,在VS中,如果在建立原始檔時什麼都不給,預設是。cpp。

3、返回值

C語言中,如果一個函式沒有指定返回值型別,預設返回int型別;C++中,如果一個函式沒有返回值則必須指定為void。

C語言和C+的區別和聯絡

4、引數列表

在C語言中,函式沒有指定引數列表時,預設可以接收任意多個引數;但在C++中,因為嚴格的引數型別檢測,沒有引數列表的函式,預設為 void,不接收任何引數。

5、預設引數

預設引數是宣告或定義函式時為函式的引數指定一個預設值。在呼叫該函式時,如果沒有指定實參則採用該預設值,否則使用指定的參。(C語言不支援預設引數)

· 半預設引數

· 全預設引數

注意:

· 在半預設的情況下,帶預設值的引數必須放在引數列表的最後面。

· 預設引數不能同時在函式的宣告和函式定義中出現,二者只能選其一。

· 預設值必須是常量或者全域性變數。

· 預設引數必須透過值參或常參傳遞。

6、函式過載

函式過載:函式過載是函式的一種特殊情況,指在同一作用域中,宣告幾個功能類似的同名函式,這些同名函式的形參列表(引數個數、型別、順序)必須不同,返回值型別可以相同也可以不同,常用來處理實現功能類似資料型別不同的問題。(C語言沒有函式過載,C++支援函式過載)。

C語言和C+的區別和聯絡

C語言中產生函式符號的規則是根據名稱產生,這也就註定了c語言不存在函式過載的概念。而C++生成函式符號則考慮了函式名、引數個數、引數型別。需要注意的是函式的返回值並不能作為函式過載的依據,也就是說int sum和double sum這兩個函式是不能構成過載的!

我們的函式過載也屬於多型的一種,這就是所謂的靜多型。

靜多型:函式過載,函式模板

動多型(執行時的多型):繼承中的多型(虛擬函式)。

使用過載的時候需要注意作用域問題:請看如下程式碼。

C語言和C+的區別和聯絡

我在全域性作用域定義了兩個函式,它們由於引數型別不同可以構成過載,此時main函式中呼叫則可以正確的呼叫到各自的函式。

但是請看main函式中被註釋掉的一句程式碼。如果將它放出來,則會提出警告:將double型別轉換成int型別可能會丟失資料。

這就意味著我們編譯器針對下面兩句呼叫都呼叫了引數型別int的compare。由此可見,編譯器呼叫函式時優先在區域性作用域搜尋,若搜尋成功則全部按照該函式的標準呼叫。若未搜尋到才在全域性作用域進行搜尋。

總結:C語言不存在函式過載,C++根據函式名引數個數引數型別判斷過載,屬於靜多型,必須同一作用域下才叫過載。

C語言和C+的區別和聯絡

7、const

C語言中被const修飾的變數不是常量,叫做常變數或者只讀變數,這個常變數是無法當作陣列下標的。然而在C++中const修飾的變數可以當作陣列下標使用,成為了真正的常量,這就是C++對const的擴充套件。

C語言中的const:被修飾後不能做左值,可以不初始化,但是之後沒有機會再初始化。不可以當陣列的下標,可以透過指標修改。

簡單來說,它和普通變數的區別只是不能做左值而已,其他地方都是一樣的。

C++中的const:真正的常量。定義的時候必須初始化,可以用作陣列的下標。const在C++中的編譯規則是替換(和宏很像),所以它被看作是真正的常量。也可以透過指標修改。需要注意的是,C++的指標有可能退化成C語言的指標。比如以下情況:

這時候的a就只是一個普通的C語言的const常變量了,已經無法當陣列的下標了。(引用了一個編譯階段不確定的值)

const在生成符號時,是local符號。即在本檔案中才可見。如果非要在別的檔案中使用它的話,在檔案頭部宣告:externcosnt int data = 10;這樣生成的符號就是global符號。

總結:C中的const叫只讀變數,只是無法做左值的變數;C++中的const是真正的常量,但也有可能退化成c語言的常量,預設生成local符號。

C語言和C+的區別和聯絡

8、引用

說到引用,我們第一反應就是想到了他的兄弟:指標。

引用從底層來說和指標就是同一個東西,但是在編譯器中它的特性和指標完全不同。

首先定義一個變數a = 10,然後我們分別定義一個引用b以及一個指標p指向a。我們來轉到反彙編看看底層的實現:

C語言和C+的區別和聯絡

可以看到底層實現完全一致,取a的地址放入eax暫存器,再將eax中的值存入引用b/指標p的記憶體中。至此我們可以說(在底層)引用本質就是一個指標。

瞭解了底層實現,我們回到編譯器。我們看到對a的值的修改,指標p的做法是*p = 20;即進行解引用後替換值。

再來看看引用修改:

我們看到修改a的值的方法也是一樣的,也是解引用。只是我們在呼叫的時候有所不同:呼叫p時需要*p解引用,b則直接使用就可以。由此我們 推斷出:引用在直接使用時是指標解引用。p直接使用則是它自己的地址。

這樣我們也瞭解了,我們給引用開闢的這塊記憶體是根本訪問不到的。如果直接用就直接解引用了。即使列印&b,輸出的也是a的地址。

C語言和C+的區別和聯絡

在此附上將指標轉為引用的小技巧:int *p = &a,我們將 引用符號移到左邊 將 *替換即可:int &p = a。

接下來看看如何建立陣列的引用:

我們知道,array拿出來使用的話就是陣列array的首元素地址。即是int *型別。

那麼&array是什麼意思呢?int **型別,用來指向array[0]地址的一個地址嗎?不要想當然了,&array是整個陣列型別。

那麼要定義一個數組引用,按照上面的小訣竅,先來寫寫陣列指標吧:

將右側的&對左邊的*進行覆蓋:

測試sizeof(q) = 10。我們成功建立了陣列引用。

經過上面的詳解,我們知道了引用其實就是取地址。那麼我們都知道一個立即數是沒有地址的,即

這樣的程式碼是無法透過編譯的。那如果你就是非要引用一個立即數,其實也不是沒有辦法:

即將這個立即數用const修飾一下,就可以了。為什麼呢?

這時因為被const修飾的都會產生一個臨時量來儲存這個資料,自然就有地址可取了。

C語言和C+的區別和聯絡

9、malloc,free && new,delete

這個問題很有意思,也是重點需要關注的問題。malloc()和free()是C語言中動態申請記憶體和釋放記憶體的標準庫中的函式。而new和delete是C++運算子、關鍵字。new和delete底層其實還是呼叫了malloc和free。它們之間的區別有以下幾個方面:

1)、malloc和free是函式,new和delete是運算子。

2)、malloc在分配記憶體前需要大小,new不需要。

例如:

malloc時需要指定大小,還需要型別轉換。new時不需要指定大小因為它可以從給出的型別判斷,並且還可以同時賦初始值。

C語言和C+的區別和聯絡

3)、malloc不安全,需要手動型別轉換,new不需要型別轉換。

4)、free只釋放空間,delete先呼叫解構函式再釋放空間(如果需要)。

與第條對應,如果使用了複雜型別,先析構再call operator delete回收記憶體。

5)、new是先呼叫建構函式再申請空間(如果需要)。

與第條對應,我們在呼叫new的時候(例如int *p2 = new int;這句程式碼 ),底層程式碼的實現是:首先push 4位元組(int型別的大小),隨後call operator new函式分配了記憶體。由於我們這句程式碼並未涉及到複雜型別(如類型別),所以也就沒有建構函式的呼叫。如下是operator new的原始碼,也是new實現的重要函式:

我們可以看到,首先malloc(size)申請引數位元組大小的記憶體,如果失敗(malloc失敗返回0)則進入判斷:如果_callnewh(size)也失敗的話,丟擲bad_alloc異常。_callnewh()這個函式是在檢視new handler是否可用,如果可用會釋放一部分記憶體再返回到malloc處繼續申請,如果new handler不可用就會丟擲異常。

C語言和C+的區別和聯絡

6)、記憶體不足(開闢失敗)時處理方式不同。

malloc失敗返回0,new失敗丟擲bad_alloc異常。

7)、new和malloc開闢記憶體的位置不同。

malloc開闢在堆區,new開闢在自由儲存區域。

8)、new可以呼叫malloc(),但malloc不能呼叫new。

new就是用malloc()實現的,new是C++獨有malloc當然無法呼叫。

10、作用域

C語言中作用域只有兩個:區域性,全域性。C++中則是有:區域性作用域,類作用域,名字空間作用域三種。

所謂名字空間就是namespace,我們定義一個名字空間就是定義一個新作用域。訪問時需要以如下方式訪問(以std為例)

例如我們有一個名字空間叫Myname,其中有一個變數叫做data。如果我們希望在其他地方使用data的話,需要在檔案頭宣告:using Myname::data;這樣一來data就使用的是Myname中的值了。可是這樣每個符號我們都得宣告豈不是累死?

C語言和C+的區別和聯絡

我們只要using namespace Myname;就可以將其中所有符號匯入了。

這也就是我們經常看到的using namespace std;的意思啦。

不學C語言能直接學C++嗎?

還是像前面所說,C++程式語言的第一大重要組成部分就是「面向過程程式設計」,而這正是C語言老大哥的領域。即使沒有學過C語言,一上來就直接學習C++的小夥伴,應該也難逃『面向過程』這一部分的內容。

從理論上來說,學C++前並不一定非得學C語言,但是有C語言底子再去學C++往往更具優勢,最起碼「面向過程程式設計」這一部分內容能夠輕車熟路。

end

一口Linux

【課程推薦】

針對應屆生和轉行的朋友,彭老師錄製了

基於Linux的物聯網綜合實戰課程