C和C++的區別(上)

cc++的區別先總結4點

1.  函數的默認值

C++可以給函數定義或聲明時給參數賦初值

(c語言後來制定的c99標準也能給默認值,但由於現在普遍學習的是c89標準,因此沒有此功能)

使用默認值的好處是什麼?

使用默認值的原理又是什麼?

使用默認值有何規則限定?請接着看。

比如main函數先定義倆變量

int a;

int b;

然後調用sum(a,b)的時候

壓入參數的時候的時候執行的彙編就是

Move  eaxdword ptr[ebp-8]

Push eax

 

Move  ecx,dword ptr[ebp-4]

Push ecx

可以看出壓入參數的順序先ba的。是從右往左的

如果在定義或者聲明的時候給函數的參數了默認值,再或者調用時直接參數給了立即數

比如 Int sum (int a,int b=10);這樣

到時候執行main的調用sum這行的彙編就會變成

 

Push  0Ah

Move  ecx,dword ptr[ebp-4]

Push ecx

可以看到少了一行彙編指令,直接把默認值作爲立即數壓入。

無疑是提高了效率

 

 

那麼使用默認值的規則是什麼。

剛纔從原理上看到了,參數壓棧的順序在底層的彙編體現是從右往左壓的。

因此默認值的的給定也必須從右往左給,可以少給,但不可以跳着給。

比如sum2(int a,int b,int c);這個函數可以

sum2(int a,int b,int c=3);

sum2(int a,int b=2,int c=3);

sum2(int a=1,int b=2,int c=3);

其中任意一種給發。

 

但絕不能這樣給

sum2(int a=1,int b,int c);

sum2(int a=1,int b,int c=3);

sum2(int a,int b=2,int c);

爲什麼不能以上這些給發

比如sum2(int a,int b=2,int c);這樣給。是不是以爲着調用的時候

必須這樣sum2(1,,3);這顯然違反語法的。

剛只是用聲明舉例。

然而實際上是可以聲明和定義都可以給,但不能給重複,一個參數的默認值只能給一次

 例如

Int sum(int a,int b=20);

Int sum(int a=10,int b);

兩次聲明是可以的。

單獨來看並不符合Int sum(int a=10,int b);剛說的從右至左的給默認值規則。

但由於它的上一行存在Int sum(int a,int b=20);已對b給過默認值了。因此再只給a默認值並不違法。


如果

Int sum(int a,int b=20);

Int sum(int a,int b=20);

這樣是不可以的

 

 

最後值得注意的是,剛纔說的都是聲明與定義在main函數上方,因爲編譯時的順序是一行一行從上往下讀的,如果這樣給定默認值,顯然也是不對的。

例如

Int  sum2(int a,int b,int c=3);

 

Main()

{

....

Sum2(1)

...

}

 

Sum2(int a,int b=2,int c);

 

 

 

 

2.  內斂函數inline

   內聯函數是在編譯時 在調用點把內聯函數的代碼展開在調用點處,並不會生成函數的符號。

   例如

   Inline Int sum (int a,int b)

{return a+b;}

Main()

{

Int x=10;

Int y=20;

Sum(x,y);

 

}

 

編譯時就是

Main()

{

Int x=10;

Int y=20;

x+y;

}

並不會給sum函數開闢個新棧,而是直接的代碼替換

  

   這麼看來內聯函數和宏非常的相似。那麼既然內斂叫內斂,宏叫宏,那麼肯定有區別所在。

區別就是 宏是單純的字符替換,在預編譯的階段,不會做任何的詞法解析,類型檢查,也就是說宏出錯的可能性非常高,不安全。而inline內斂函數是在編譯時期,會進行詞法解析,類型檢查,詞法、類型有誤就會編譯失敗。

所以說,inline相當於安全版的宏

 

那麼既然內斂函數名字中有個函數,那麼它和函數有是否有本質區別呢?

答案是肯定的。

首先一個是在調用方的代碼替換,一個是另開闢新棧的相對獨立執行。

其次一般函數會生成符號,而內斂函數本質來講只是個安全宏,所以不會生成符號。

 

那麼既然內斂函數這麼安全,也不用開闢新棧效率高,爲什麼不定義所有的函數爲內斂函數呢?

首先內斂函數只對本文件可見,且編譯時不生成符號,這對於跨文件的工程來說,顯然是不好的。

其次,如果多次使用內聯函數,將進行大量的代碼替換,造成.txt段過於冗餘。

最後我們說的效率高,是在函數的執行開銷小於棧幀的開闢開銷的情況下成立的。

也就是說假如代碼只有簡單的短短几行,用內斂的確能提高效率。

但如果函數代碼過於複雜,內斂的效率還是沒有一般函數高的。

 

最後值得注意的是內斂函數只算一個給編譯器的建議,也就是說編譯器可能不會使用內斂,因爲某些函數使用代碼替換會導致錯誤,比如遞歸函數,遞歸的核心就是開闢棧幀遞歸數據然後層層計算最後收尾,簡單的代碼替換怎麼能確定遞歸的尾巴?

並且inline只在release版本生效,debug版本是不啓作用的。



3.函數的重載

      c語言中函數的定義不能重名,如果重名就會出現重定義,因爲函數生成的符號只由函數名決定。

但是c++中的函數定義可以重名,但是重名的前提是函數的參數不能完全相同,因爲c++的函數符號是由函數名和參數類型決定的。

     我們都知道在不同作用域可以定義相同名字的變量。比如main中的局部變量和本文件的全局變量就可以同名。並且main中優先使用的是離自己作用域最近局部變量。

int sum(int a,int b);

 int main()

 { 

int sum(char *a,char *p);

   sum(1,2);

 return 0;

}

 

例如這樣就會出錯,sum(1,2)只會看到int sum(char *a,char *p);

而無法看到int sum(int a,int b);

 所以說函數重載必須在同一個作用域內

 還有一個點就是實參和形參的轉化問題。

我們先回一下c語言的類型轉換



箭頭所指的線路都是編譯器默認會選擇的優先轉向。

比如float類型變量參與運算時,都會默認轉換成double型,然後參與運算,目的是爲了提高精度。


舉例

  比如short  a=-1 usigned int  b=1比較 。

 看似b比較大吧,但其實結果我們知道的,ab比較時結果卻是a比較大,這是爲什麼?我們看a>b 這個式子,ashortbu_int ,按道理a要先轉化成int型,這時是仍爲-1,然後發現busigned,繼續轉化爲usigned,然而usigned中不可能有-1,我們知道-1應該是很大的數,即2^32-1 。所以a>b這個式子其實最後值是1,即ab大。

 

可是如果不是箭頭所指呢,即我定義的函數形參是float,然後重載一個形參爲int的。

最後我傳入實參爲double型,編譯器就不知道double是該優先轉成float還是int,因此就會有問題。


4.  cc++函數互相調用

之前也說了,cc++的函數生成符號不一樣,那麼c++c在互相調用對方函數的時候就會出錯,因爲根本無法找到對應的符號。

而由於c++是基於c語言而來,肯定會想到兼容性的問題,因此如果c++想要使用c的函數,c++會有特殊處理,可以把函數符號轉化成c的不帶參數的形式,只用在c++文件裏這樣

  Extern C

{

 Int sum(int ,int )

}

這樣編譯時同時也會生成c語言版本的函數符號,到時候c++想要使用c中定義的函數也可以找到符號。

 

但是反過來呢,如果c想要使用c++文件裏定義的函數呢。c可是不會兼容c++的,c可不能在自己文件裏使用externc++這種東西。

那麼該怎麼做呢?

其實可以創建一個接口文件。

比如這是cpp文件

Sum.cpp

 

Int sum(int a,int b)

{

Return a+b;

}

 

這是.c文件

Main()

{

Int a=10;

Int b=20;

sum(a,b);

}

目前來看,.c文件是絕對使用不了.cpp裏的sum定義的。

那麼這時就要新建一個mid.cpp接口文件了

這個文件內容如下

Mid.cpp

Int sum(int a,int b)

externC

{

  Mysum(int a,int b)

{

 return sum(a,b);

}

}

首先mid文件中先聲明瞭sum.cppsum函數,由於都是cpp文件,因此sum對於mid是可見的,那麼mid就可以看到sum的定義體,那麼接下來mid文件只要把這個sum函數封裝一下,並且用externC生成C符號的形式封裝出一個函數,這個函數的功能和sum完全一樣,參數一樣,返回值也一樣,這不就能讓c使用c++的函數了嗎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章