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產生影響。