1. 程式人生 > >c/c++區別(一)函式的預設值 函式過載 行內函數 c/c++介面呼叫 const在c/c++的區別

c/c++區別(一)函式的預設值 函式過載 行內函數 c/c++介面呼叫 const在c/c++的區別

c/c++ 的區別

一.函式的預設值

在C語言裡函式的引數是不能夠帶預設值的。比如int func(int a, int b = 1);這樣的宣告就是不正確的。但是在C++中上述的宣告是被允許的

 

函式的預設引數值,即在定義引數的時候同時給它一個初始值。在呼叫函式的時候,我們可以省略含有預設值的引數。也就是說,如果使用者指定了引數值,則使用使用者指定的值,否則使用預設引數的值。

 

1.預設值一般寫在宣告中(一般宣告在呼叫之前(宣告 呼叫 定義為一般位置) 若有實參 實參會替代預設值 若無實參 則用預設值)

#include <iostream>

using namespace std;

int max(int a, int b, int c=0);//函式宣告,形參c有預設值

 

int main( )
{
   int a,b,c;
    cin>>a>>b>>c;
    cout<<″max(a,b,c)=″<<max(a,b,c)<<endl;   //輸出3個數中的最大者
    cout<<″max(a,b)=″<<max(a,b)<<endl;       //輸出2個數中的最大者
    return 0;
}
int max(int a,int b,int c)        //函式定義

{

if(b>a) a=b;

 if(c>a) a=c;
     return a;
}
執行情況如下:
14  -56  135↙
max(a,b,c)=135
max(a,b)=14

 

若在定義時而不是在宣告時置預設值,那麼函式定義一定要在函式的呼叫之前。因為宣告時已經給編譯器一個該函式的嚮導,所以只在定義時設預設值時,編譯器只有檢查到定義時才知道函式使用了預設值。若先呼叫後定義,在呼叫時編譯器並不知道哪個引數設了預設值。所以我們通常是將預設值的設定放在宣告中而不是定義中。

2.自右向左依此賦值

    

3.預設值只能賦一次(否則編譯器會產生二義性 會報錯)

4.預設值的限制

 (1)不能使用區域性變數(不符合語法規則 呼叫時看不到下面定義的區域性變數

 (2)能使用全域性變數(靜態全域性變數也可以)

 (3)能使用函式

 

二.函式過載

在實際開發中,有時候我們需要實現幾個功能類似的函式,只是有些細節不同。例如希望交換兩個變數的值,這兩個變數有多種型別,可以是 int、float、char、bool 等,我們需要通過引數把變數的地址傳入函式內部。在C語言中,程式設計師往往需要分別設計出三個不同名的函式

但在C++中,這完全沒有必要。C++ 允許多個函式擁有相同的名字,只要它們的引數列表不同就可以,這就是函式的過載(Function Overloading)。藉助過載,一個函式名可以有多種用途。

引數列表又叫引數簽名,包括引數的型別、引數的個數和引數的順序,只要有一個不同就叫做引數列表不同。

 

過載就是在一個作用範圍內(同一個類、同一個名稱空間等)有多個名稱相同但引數不同的函式。過載的結果是讓一個函式名擁有了多種用途,使得命名更加方便(在中大型專案中,給變數、函式、類起名字是一件讓人苦惱的問題),呼叫更加靈活。

 

在使用過載函式時,同名函式的功能應當相同或相近,不要用同一函式名去實現完全不相干的功能,雖然程式也能執行,但可讀性不好,使人覺得莫名其妙。

 

注意,引數列表不同包括引數的個數不同、型別不同或順序不同,僅僅引數名稱不同是不可以的。函式返回值也不能作為過載的依據。

 

1.c++函式符號生成的規則

(1)返回值 (2)函式名 (3)引數列表(個數 型別 順序)

 c語言中不允許存在函式名相同的函式 而c++是允許的 兩種語言函式符號生成規則不同

2.函式過載的三要素規則

(1)同作用域(同一個類、同一個名稱空間等)

(2)函式名稱必須相同

   (3)引數列表必須不同(個數不同 型別不同 引數排列順序不同等)。

函式返回型別可以相同也可以不相同,僅僅返回型別不同不足以成為函式的過載。

C++的符號生成規則為函式過載提供了支援 但函式過載不依賴與符號生成規則(函式過載與返回值無關)

3.注意事項:

函式名相同 引數列表相同返回值不同不能構成函式過載;

一個函式不能既做預設值 又做函式過載;

 

C++ 是如何做到函式過載的

C++程式碼在編譯時會根據引數列表對函式進行重新命名,例如void Swap(int a, int b)會被重新命名為_Swap_int_int,void Swap(float x, float y)會被重新命名為_Swap_float_float。當發生函式呼叫時,編譯器會根據傳入的實參去逐個匹配,以選擇對應的函式,如果匹配失敗,編譯器就會報錯,這叫做過載決議(Overload Resolution)。

 

三.Inline行內函數

行內函數:行內函數是浪費空間來節省時間的設定,因為函式的呼叫是很浪費時間的,寫成行內函數可以在每次呼叫時用函式體內容代替函式呼叫,有點類似一個巨集定義。當函式體語句較少,且沒有複雜的迴圈語句,且呼叫次數較多時,就可以用行內函數。

行內函數處理在編譯階段 比巨集更安全;展開函式不用清棧和開棧 沒有函式壓棧開銷 行內函數提升程式執行的效率。因此,行內函數的執行速度比常規函式稍快,但代價是需要佔用更多記憶體。關鍵字inline必須與函式定義體放在一起才能使函式稱為內聯

 

對於經常要使用的程式碼段,為了方便使用會將其封裝成函式。然而在呼叫函式時會建立棧幀,增加了額外的開銷。為了節省開銷,在C語言中會使用巨集替換。然而巨集具有一些缺點:

(1)不能除錯;

(2)由於巨集使用簡單的文字替換,對於有些情況,在同一個作用域中同一個巨集使用兩次會出現重定義錯誤。

 

行內函數的設計位置:一般寫在標頭檔案下

問:為什麼不把所有函式都處理成行內函數?

    如果所有函式都處理成行內函數 都展開生成的可執行檔案的程式碼太大 開銷太大 典型的空間換時間

 

1.行內函數以程式碼膨脹為代價(以空間換時間)

      當函式堆疊呼叫 > 函式執行的開銷 此時程式碼少 函式體小 因此建議使用行內函數;

      當函式堆疊呼叫 < 函式執行的開銷 此時程式碼多 不建議使用行內函數;

2.比較inline行內函數與static函式

  (1)inline函式無開棧清棧過程 而static函式有開棧清棧過程

  (2)inline函式不生成函式符號 而static生成函式的區域性符號(僅在當前檔案可見)

3.行內函數的注意事項

  (1)inline函式對編譯器而言只是一個建議

  (2)如果定義的函式體內有遞迴 迴圈while switch等 編譯器優化時會自動忽略掉內聯

 (3)inline函式在debug版本下不生效 在release版本下生效

 (4)inline函式是基於實現的 不是基於宣告的

 

 

四.c/c++介面的相互呼叫

   extern "C"是C++的特性,是一種連結約定,它並不影響呼叫函式的定義,即使做了該宣告,對函式型別的檢查和引數轉換仍要遵循C++的標準,而不是C。主要是為了解決C++在呼叫C函式庫時,用C++直接連結就會出現不能識別符號的問題,而用上extern "C"後,告訴C++編譯器要以C語言的方式編譯和連結函式,即直接使用函式名而不是一個經過處理的函式名。

  int Sum( int a , int b );   .c檔案產生的是_Sum

  int Sum( int a , int b );   .cpp檔案產生的是_Sum_int_int

 

1.c++呼叫c的介面

   在.cpp檔案中加extern “C”

2.c呼叫c++的介面

(1)修改c++檔案

    .cpp檔案中加extern”C”

(2)不修改c++檔案

   加中間層處理

別人寫好的連結庫show.so 是不能修改的 沒法直接呼叫連結庫 於是加上中間層即可解決 中間層是我們自己寫的.cpp檔案 這時就可以修改了 呼叫show函式即可 並加上extern”C” 此時.cpp檔案轉換成了.c檔案 在main.c裡在呼叫Myshow()即可

3.原始檔不確定什麼編譯器編譯

 加巨集 :#ifdef  __cplusplus

 

五.c/c++中const的區別

1.在c語言中 const修飾的是常變數 

   常變數不能做左值 其他和普通變數的處理方式相同(例如解引用 修改值等功能 只是不能做左值而已)

2.在c++中 const修飾的是常量

(1)常量不允許修改 一定要初始化值

(2)不允許普通的指標指向(有風險被解引用從而被修改)

因此要用常量指標指向:const int * p = &a;//不允許修改值

 

     對比:

const int *a= &b;//const 在*左邊,指標指向的內容為常量 即a的內容為常量 不可修改值

 

int *const a=&b;//const在*號的右邊,表明指標a是常量不能進行修改,但是a指的內容是可以修改的

 

const int *const a=&b;//const在*號的兩邊都有,表明指標a是常量不能進行修改,但是a指的內容是也不可以修改的 在編譯階段

把用到常量的地方替換成常量初始化的值 因此不管中間再怎麼人為修改 也不會改變值 因此很安全

 

c++中用const定義了一個常量後,不會分配一個空間給它,而是將其寫入符號表(symbol table),這使得它成為一個編譯期間的常量,沒有了儲存與讀記憶體的操作,使得它的效率也很高。

在編譯階段把用到常量的地方替換成常量初始化的值 因此不管中間再怎麼人為修改 也不會改變值 因此很安全

通過 int*p = (int*)(&a);這種方法,可以直接修改const常量對應的記憶體空間中的值,但是這種修改不會影響到常量本身的值,因為用到a的時候,編譯器根本不會去進行記憶體空間的讀取。這就是c++的常量摺疊(constant folding),即將const常量放在符號表中,而並不給其分配記憶體。編譯器直接進行替換優化。除非需要用到a的儲存空間的時候,編譯器迫不得已才會分配一個空間給a,但之後a的值仍舊從符號表中讀取,不管a的儲存空間中的值如何變化,都不會對常量a產生影響。