深入淺析C語(yǔ)言與C++的區(qū)別與聯(lián)系
C語(yǔ)言雖說(shuō)經(jīng)常和C++在一起被大家提起,但可千萬(wàn)不要以為它們是一種編程語(yǔ)言。我們來(lái)介紹C語(yǔ)言和C++中的區(qū)別和聯(lián)系。
首先C++和C語(yǔ)言本來(lái)就是兩種不同的編程語(yǔ)言,但C++確實(shí)是對(duì)C語(yǔ)言的擴(kuò)充和延伸,并且對(duì)C語(yǔ)言提供后向兼容的能力。對(duì)于有些人說(shuō)的C++完全就包含了C語(yǔ)言的說(shuō)法還是有點(diǎn)別扭的。
一、C語(yǔ)言是面向過(guò)程語(yǔ)言,而C++是面向?qū)ο笳Z(yǔ)言
我們都知道C語(yǔ)言是面向過(guò)程語(yǔ)言,而C++是面向?qū)ο笳Z(yǔ)言,說(shuō)C和C++的區(qū)別,也就是在比較面向過(guò)程和面向?qū)ο蟮膮^(qū)別。
1、面向過(guò)程和面向?qū)ο蟮膮^(qū)別
面向過(guò)程:面向過(guò)程編程就是分析出解決問(wèn)題的步驟,然后把這些步驟一步一步的實(shí)現(xiàn),使用的時(shí)候一個(gè)一個(gè)的依次調(diào)用就可以了。
面向?qū)ο螅好嫦驅(qū)ο缶幊叹褪前褑?wèn)題分解成各個(gè)對(duì)象,建立對(duì)象的目的不是為了完成一個(gè)步驟,而是為了描述某個(gè)事物在整個(gè)解決問(wèn)題的步驟中的行為。
2、面向過(guò)程和面向?qū)ο蟮膬?yōu)缺點(diǎn)
在學(xué)習(xí)一些比較抽象的概念時(shí),由于我們的理解能力很有限,有時(shí)候一些比較恰當(dāng)?shù)睦右彩怯兄谖覀儗W(xué)習(xí)的,因此對(duì)二者的優(yōu)缺點(diǎn)比較,還是先舉例子,后總結(jié)吧!
(1)用面向過(guò)程寫(xiě)出來(lái)的程序就像一份蛋炒飯,也就是米飯和炒的菜均勻的混合在了一起,因此蛋炒飯入味均勻,不會(huì)像蓋澆飯那樣,可能有時(shí)候吃的菜多飯少,還有時(shí)候菜少飯多。但是如果你不喜歡吃蛋炒飯,只想吃肉炒飯,那么原來(lái)做的這份蛋炒飯就得倒掉了,重新做一份肉炒飯,廚師就得辛苦了!
(2)用面向?qū)ο髮?xiě)出來(lái)的程序就像一份蓋澆飯,也就是米飯和蓋菜分別做好,將蓋菜放在米飯上面,蓋澆飯雖然沒(méi)有蛋炒飯那樣入味均勻,但是如果給了你一份土豆絲蓋飯,你又不想吃了,換成牛肉蓋飯,廚師只需要將米飯上面的土豆絲倒掉,重新做一份牛肉放在上面就好了。
那么到底蛋炒飯和蓋澆飯哪個(gè)好吃呢?
誰(shuí)也不能說(shuō)到底哪個(gè)好,畢竟蛋炒飯的餐館和蓋澆飯的餐館都很多,而且生意都很不錯(cuò),存在即為合理!
如果非要將二者進(jìn)行一個(gè)高地的比較的話,那就得先設(shè)定一個(gè)場(chǎng)景了!
蓋澆飯的好處就是”菜”“飯”分離,從而提高了制作蓋澆飯的靈活性。飯不滿意就換飯,菜不滿意換菜。用專業(yè)術(shù)語(yǔ)來(lái)說(shuō)就是”可維護(hù)性“較好,”飯” 和”菜”的耦合度比較低。
蛋炒飯將”蛋”“飯”攪和在一起,想換”蛋”“飯”中任何一種都很困難,耦合度很高,以至于”可維護(hù)性”比較差。
二者的簡(jiǎn)單總結(jié)如下:
面向過(guò)程語(yǔ)言
優(yōu)點(diǎn):性能比面向?qū)ο蟾?,因?yàn)轭?lèi)調(diào)用時(shí)需要實(shí)例化,開(kāi)銷(xiāo)比較大,比較消耗資源;比如單片機(jī)、嵌入式開(kāi)發(fā)、 Linux/Unix等一般采用面向過(guò)程開(kāi)發(fā),性能是最重要的因素。缺點(diǎn):沒(méi)有面向?qū)ο笠拙S護(hù)、易復(fù)用、易擴(kuò)展
面向?qū)ο笳Z(yǔ)言
優(yōu)點(diǎn):易維護(hù)、易復(fù)用、易擴(kuò)展,由于面向?qū)ο笥蟹庋b、繼承、多態(tài)性的特性,可以設(shè)計(jì)出低耦合的系統(tǒng),使系統(tǒng) 更加靈活、更加易于維護(hù)缺點(diǎn):性能比面向過(guò)程低
二、具體語(yǔ)言上的區(qū)別
1、關(guān)鍵字的不同
C語(yǔ)言有32個(gè)關(guān)鍵字
C++有63個(gè)關(guān)鍵字
2、后綴名不同
C源文件后綴.c,C++源文件后綴.cpp,在VS中,如果在創(chuàng)建源文件時(shí)什么都不給,默認(rèn)是.cpp。
3、返回值
C語(yǔ)言中,如果一個(gè)函數(shù)沒(méi)有指定返回值類(lèi)型,默認(rèn)返回int類(lèi)型;C++中,如果一個(gè)函數(shù)沒(méi)有返回值則必須指定為void。
4、參數(shù)列表
在C語(yǔ)言中,函數(shù)沒(méi)有指定參數(shù)列表時(shí),默認(rèn)可以接收任意多個(gè)參數(shù);但在C++中,因?yàn)閲?yán)格的參數(shù)類(lèi)型檢測(cè),沒(méi)有參數(shù)列表的函數(shù),默認(rèn)為 void,不接收任何參數(shù)。
5、缺省參數(shù)
缺省參數(shù)是聲明或定義函數(shù)時(shí)為函數(shù)的參數(shù)指定一個(gè)默認(rèn)值。在調(diào)用該函數(shù)時(shí),如果沒(méi)有指定實(shí)參則采用該默認(rèn)值,否則使用指定的參。(C語(yǔ)言不支持缺省參數(shù))
半缺省參數(shù)
void FunTest(int _iParam1, int _iParam2 = 0 ) {} void FunTest(int _iParam1, int _iParam2 = 0 , int _iParam3/* = 0*/) {} void FunTest(int _iParam1, int _iParam2 /* = 0*/,int _iParam3 = 0) {}
全缺省參數(shù)
void FunTest(int _iParam1 = 0, int _iParam = 1) { } //注意:慎用缺省函數(shù),否則會(huì)產(chǎn)生二義性 void FunTest () {} void FunTest (int a = 10 ) {} //假如使用不帶實(shí)參方式調(diào)用FunTest()函數(shù)時(shí),編譯器將不知道調(diào)用哪一個(gè),產(chǎn)生二義性
注意:
- 在半缺省的情況下,帶缺省值的參數(shù)必須放在參數(shù)列表的最后面。
- 缺省參數(shù)不能同時(shí)在函數(shù)的聲明和函數(shù)定義中出現(xiàn),二者只能選其一。
- 缺省值必須是常量或者全局變量。
- 缺省參數(shù)必須通過(guò)值參或常參傳遞。
6、函數(shù)重載
函數(shù)重載:函數(shù)重載是函數(shù)的一種特殊情況,指在同一作用域中,聲明幾個(gè)功能類(lèi)似的同名函數(shù),這些同名函數(shù)的形參列表(參數(shù)個(gè)數(shù)、類(lèi)型、順序)必須不同,返回值類(lèi)型可以相同也可以不同,常用來(lái)處理實(shí)現(xiàn)功能類(lèi)似數(shù)據(jù)類(lèi)型不同的問(wèn)題。(C語(yǔ)言沒(méi)有函數(shù)重載,C++支持函數(shù)重載)。
C語(yǔ)言中產(chǎn)生函數(shù)符號(hào)的規(guī)則是根據(jù)名稱產(chǎn)生,這也就注定了c語(yǔ)言不存在函數(shù)重載的概念。而C++生成函數(shù)符號(hào)則考慮了函數(shù)名、參數(shù)個(gè)數(shù)、參數(shù)類(lèi)型。需要注意的是函數(shù)的返回值并不能作為函數(shù)重載的依據(jù),也就是說(shuō)int sum和double sum這兩個(gè)函數(shù)是不能構(gòu)成重載的!
我們的函數(shù)重載也屬于多態(tài)的一種,這就是所謂的靜多態(tài)。
- 靜多態(tài):函數(shù)重載,函數(shù)模板
- 動(dòng)多態(tài)(運(yùn)行時(shí)的多態(tài)):繼承中的多態(tài)(虛函數(shù))。
使用重載的時(shí)候需要注意作作用域問(wèn)題:請(qǐng)看如下代碼。
#include <iostream> using namespace std; bool compare(int a,int b) { return a > b; } bool compare(double a,double b) { return a > b; } int main() { //bool compare(int a,int b); compare(10,20); compare(10.5,20.5); return 0; }
我在全局作用域定義了兩個(gè)函數(shù),它們由于參數(shù)類(lèi)型不同可以構(gòu)成重載,此時(shí)main函數(shù)中調(diào)用則可以正確的調(diào)用到各自的函數(shù)。
但是請(qǐng)看main函數(shù)中被注釋掉的一句代碼。如果我將它放出來(lái),則會(huì)提出警告:將double類(lèi)型轉(zhuǎn)換成int類(lèi)型可能會(huì)丟失數(shù)據(jù)。
這就意味著我們編譯器針對(duì)下面兩句調(diào)用都調(diào)用了參數(shù)類(lèi)型int的compare。由此可見(jiàn),編譯器調(diào)用函數(shù)時(shí)優(yōu)先在局部作用域搜索,若搜索成功則全部按照該函數(shù)的標(biāo)準(zhǔn)調(diào)用。若未搜索到才在全局作用域進(jìn)行搜索。
總結(jié):C語(yǔ)言不存在函數(shù)重載,C++根據(jù)函數(shù)名參數(shù)個(gè)數(shù)參數(shù)類(lèi)型判斷重載,屬于靜多態(tài),必須同一作用域下才叫重載。
7、const
C語(yǔ)言中被const修飾的變量不是常量,叫做常變量或者只讀變量,這個(gè)常變量是無(wú)法當(dāng)作數(shù)組下標(biāo)的。然而在C++中const修飾的變量可以當(dāng)作數(shù)組下標(biāo)使用,成為了真正的常量。這就是C++對(duì)const的擴(kuò)展。
C語(yǔ)言中的const:被修飾后不能做左值,可以不初始化,但是之后沒(méi)有機(jī)會(huì)再初始化。不可以當(dāng)數(shù)組的下標(biāo),可以通過(guò)指針修改。簡(jiǎn)單來(lái)說(shuō),它和普通變量的區(qū)別只是不能做左值而已。其他地方都是一樣的。
C++中的const:真正的常量。定義的時(shí)候必須初始化,可以用作數(shù)組的下標(biāo)。const在C++中的編譯規(guī)則是替換(和宏很像),所以它被看作是真正的常量。也可以通過(guò)指針修改。需要注意的是,C++的指針有可能退化成C語(yǔ)言的指針。
比如以下情況:
int b = 20; const int a = b;
這時(shí)候的a就只是一個(gè)普通的C語(yǔ)言的const常變量了,已經(jīng)無(wú)法當(dāng)數(shù)組的下標(biāo)了。(引用了一個(gè)編譯階段不確定的值)
const在生成符號(hào)時(shí),是local符號(hào)。即在本文件中才可見(jiàn)。
如果非要在別的文件中使用它的話,在文件頭部聲明:extern cosnt int data = 10;
這樣生成的符號(hào)就是global符號(hào)。
總結(jié)
C中的const叫只讀變量,只是無(wú)法做左值的變量;
C++中的const是真正的常量,但也有可能退化成c語(yǔ)言的常量,默認(rèn)生成local符號(hào)。
8、引用
說(shuō)到引用,我們第一反應(yīng)就是想到了他的兄弟:指針。引用從底層來(lái)說(shuō)和指針就是同一個(gè)東西,但是在編譯器中它的特性和指針完全不同。
int a = 10; int &b = a; int *p = &a; //b = 20; //*p = 20;
首先定義一個(gè)變量a = 10,然后我們分別定義一個(gè)引用b以及一個(gè)指針p指向a。
我們來(lái)轉(zhuǎn)到反匯編看看底層的實(shí)現(xiàn):
可以看到底層實(shí)現(xiàn)完全一致,取a的地址放入eax寄存器,再將eax中的值存入引用b/指針p的內(nèi)存中。至此我們可以說(shuō)(在底層)引用本質(zhì)就是一個(gè)指針。
了解了底層實(shí)現(xiàn),我們回到編譯器。我們看到對(duì)a的值的修改,指針p的做法是*p = 20;即進(jìn)行解引用后替換值。底層實(shí)現(xiàn):
再來(lái)看看引用修改:
我們看到修改a的值的方法也是一樣的,也是解引用。只是我們?cè)谡{(diào)用的時(shí)候有所不同:調(diào)用p時(shí)需要*p解引用,b則直接使用就可以。由此我們 推斷出:引用在直接使用時(shí)是指針解引用。p直接使用則是它自己的地址。
這樣我們也了解了,我們給引用開(kāi)辟的這塊內(nèi)存是根本訪問(wèn)不到的。如果直接用就直接解引用了。即使打印&b,輸出的也是a的地址。
在此附上將指針轉(zhuǎn)為引用的小技巧:int *p = &a,我們將 引用符號(hào)移到左邊 將 *替換即可:int &p = a。
接下來(lái)看看如何創(chuàng)建數(shù)組的引用:
int array[10] = {0}; //定義一個(gè)數(shù)組
我們知道,array拿出來(lái)使用的話就是數(shù)組array的首元素地址。即是int *類(lèi)型。
那么&array是什么意思呢?int **類(lèi)型,用來(lái)指向array[0]地址的一個(gè)地址嗎?不要想當(dāng)然了,&array是整個(gè)數(shù)組類(lèi)型。
那么要定義一個(gè)數(shù)組引用,按照上面的小訣竅,先來(lái)寫(xiě)寫(xiě)數(shù)組指針吧:
int (*q) [10] = &array;
將右側(cè)的&對(duì)左邊的*進(jìn)行覆蓋:
int (&q)[10] = array;
測(cè)試sizeof(q) = 10。我們成功創(chuàng)建了數(shù)組引用。
經(jīng)過(guò)上面的詳解 ,我們知道了引用其實(shí)就是取地址。那么我們都知道一個(gè)立即數(shù)是沒(méi)有地址的,即
int &b = 10;
這樣的代碼是無(wú)法通過(guò)編譯的。那如果你就是非要引用一個(gè)立即數(shù),其實(shí)也不是沒(méi)有辦法:
const int &b = 10;
即將這個(gè)立即數(shù)用const修飾一下,就可以了。為什么呢?
這時(shí)因?yàn)楸籧onst修飾的都會(huì)產(chǎn)生一個(gè)臨時(shí)量來(lái)保存這個(gè)數(shù)據(jù),自然就有地址可取了。
9、malloc,free && new,delete
這個(gè)問(wèn)題很有意思,也是重點(diǎn)需要關(guān)注的問(wèn)題。malloc()和free()是C語(yǔ)言中動(dòng)態(tài)申請(qǐng)內(nèi)存和釋放內(nèi)存的標(biāo)準(zhǔn)庫(kù)中的函數(shù)。而new和delete是C++運(yùn)算符、關(guān)鍵字。new和delete底層其實(shí)還是調(diào)用了malloc和free。它們之間的區(qū)別有以下幾個(gè)方面:
1)、malloc和free是函數(shù),new和delete是運(yùn)算符。
2)、malloc在分配內(nèi)存前需要大小,new不需要。
例如:
int *p1 = (int *)malloc(sizeof(int));
int *p2 = new int; //int *p3 = new int(10);
malloc時(shí)需要指定大小,還需要類(lèi)型轉(zhuǎn)換。new時(shí)不需要指定大小因?yàn)樗梢詮慕o出的類(lèi)型判斷,并且還可以同時(shí)賦初始值。
3)、malloc不安全,需要手動(dòng)類(lèi)型轉(zhuǎn)換,new不需要類(lèi)型轉(zhuǎn)換。
詳見(jiàn)上一條。
4)、free只釋放空間,delete先調(diào)用析構(gòu)函數(shù)再釋放空間(如果需要)。
與第⑤條對(duì)應(yīng),如果使用了復(fù)雜類(lèi)型,先析構(gòu)再call operator delete回收內(nèi)存。
5)、new是先調(diào)用構(gòu)造函數(shù)再申請(qǐng)空間(如果需要)。
與第④條對(duì)應(yīng),我們?cè)谡{(diào)用new的時(shí)候(例如int *p2 = new int;這句代碼 ),底層代碼的實(shí)現(xiàn)是:首先push 4字節(jié)(int類(lèi)型的大?。?,隨后call operator new函數(shù)分配了內(nèi)存。由于我們這句代碼并未涉及到復(fù)雜類(lèi)型(如類(lèi)類(lèi)型),所以也就沒(méi)有構(gòu)造函數(shù)的調(diào)用。如下是operator new的源代碼,也是new實(shí)現(xiàn)的重要函數(shù):
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) { // try to allocate size bytes void *p; while ((p = malloc(size)) == 0) if (_callnewh(size) == 0) { // report no memory _THROW_NCEE(_XSTD bad_alloc, ); } return (p); }
我們可以看到,首先malloc(size)申請(qǐng)參數(shù)字節(jié)大小的內(nèi)存,如果失敗(malloc失敗返回0)則進(jìn)入判斷:如果_callnewh(size)也失敗的話,拋出bad_alloc異常。_callnewh()這個(gè)函數(shù)是在查看new handler是否可用,如果可用會(huì)釋放一部分內(nèi)存再返回到malloc處繼續(xù)申請(qǐng),如果new handler不可用就會(huì)拋出異常。
6)、內(nèi)存不足(開(kāi)辟失?。r(shí)處理方式不同。
malloc失敗返回0,new失敗拋出bad_alloc異常。
7)、new和malloc開(kāi)辟內(nèi)存的位置不同。
malloc開(kāi)辟在堆區(qū),new開(kāi)辟在自由存儲(chǔ)區(qū)域。
8)、new可以調(diào)用malloc(),但malloc不能調(diào)用new。
new就是用malloc()實(shí)現(xiàn)的,new是C++獨(dú)有malloc當(dāng)然無(wú)法調(diào)用。
10、作用域
C語(yǔ)言中作用域只有兩個(gè):局部,全局。
C++中則是有:局部作用域,類(lèi)作用域,名字空間作用域三種。
所謂名字空間就是namespace,我們定義一個(gè)名字空間就是定義一個(gè)新作用域。訪問(wèn)時(shí)需要以如下方式訪問(wèn)(以std為例)
std::cin<< "123" <<std::endl;
例如我們有一個(gè)名字空間叫Myname,其中有一個(gè)變量叫做data。
如果我們希望在其他地方使用data的話,需要在文件頭聲明:using Myname::data;
這樣一來(lái)data就使用的是Myname中的值了??墒沁@樣每個(gè)符號(hào)我們都得聲明豈不是累死?
我們只要using namespace Myname;就可以將其中所有符號(hào)導(dǎo)入了。
這也就是我們經(jīng)??吹降膗sing namespace std;的意思啦。
以上就是深入淺析C語(yǔ)言與C++的區(qū)別與聯(lián)系的詳細(xì)內(nèi)容,更多關(guān)于C語(yǔ)言與C++的區(qū)別聯(lián)系的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
visual studio 2022中的scanf問(wèn)題解決
昨天在使用Visual Studio 2022編寫(xiě)C語(yǔ)言程序時(shí)遇到了scanf問(wèn)題,對(duì)于vs的編譯器來(lái)說(shuō)scanf是不安全的,編譯器通過(guò)不了scanf,本文就來(lái)介紹一下解決方法,感興趣的可以了解一下2023-12-12C++實(shí)現(xiàn)LeetCode(148.鏈表排序)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(148.鏈表排序),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07notepad介紹及插件cmake編譯過(guò)程(替代notepad++)
這篇文章主要介紹了notepad介紹及插件cmake編譯過(guò)程(替代notepad++),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03C++類(lèi)與對(duì)象深入之靜態(tài)成員與友元及內(nèi)部類(lèi)詳解
朋友們好,這篇播客我們繼續(xù)C++的初階學(xué)習(xí),現(xiàn)在對(duì)我們對(duì)C++的靜態(tài)成員,友元,內(nèi)部類(lèi)知識(shí)點(diǎn)做出總結(jié),整理出來(lái)一篇博客供我們一起復(fù)習(xí)和學(xué)習(xí),如果文章中有理解不當(dāng)?shù)牡胤?還希望朋友們?cè)谠u(píng)論區(qū)指出,我們相互學(xué)習(xí),共同進(jìn)步2022-06-06C語(yǔ)言用函數(shù)實(shí)現(xiàn)反彈球消磚塊
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言用函數(shù)實(shí)現(xiàn)反彈球消磚塊,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易五子棋小游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單五子棋小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05C++ 數(shù)據(jù)結(jié)構(gòu)之kmp算法中的求Next()函數(shù)的算法
這篇文章主要介紹了C++ 數(shù)據(jù)結(jié)構(gòu)之kmp算法中的求Next()函數(shù)的算法的相關(guān)資料,需要的朋友可以參考下2017-06-06C語(yǔ)言實(shí)現(xiàn)繪制可愛(ài)的橘子鐘表
這篇文章主要為大家詳細(xì)介紹了如何利用C語(yǔ)言實(shí)現(xiàn)繪制可愛(ài)的橘子鐘表,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的可以了解一下2022-12-12