1. 程式人生 > >C與C++的區別(上)

C與C++的區別(上)

一、函式的預設值

int sum(int a,int b)
{
    return a+b;
}

int main()
{
    int a = 10;
    int b = 20;
    sum(a,b);
}

以上程式碼sum(a,b)的反彙編為

00C3144C  mov         eax,dword ptr [b]  
00C3144F  push        eax  
00C31450  mov         ecx,dword ptr [a]  
00C31453  push        ecx  
00C31454  call        sum (0C3105Fh)  
00C31459  add
esp,8

如果將sum函式設定預設值,即

int sum(int a,int b = 20)
{
    return a+b;
}

int main()
{
    int a = 10;
    sum(a);
    //sum(a,30);30為立即數
}

以上程式碼sum函式的反彙編為

00231445  push        14h  
00231447  mov         eax,dword ptr [a]  
0023144A  push        eax  
0023144B  call        sum (023105Fh)  
00231450  add         esp,8

與未初始化的函式相比就少了一步mov b的過程。

預設值的總結:
預設值是從右往左賦值的,絕不可以int sum(int a = 10,int b);
但是在函式宣告的時候可以這樣寫:
int sum(int a,int b=20);
int sum(int a=10,int b);
這是因為在前一行的sum函式中已經給b賦值了
但是如果將以上兩句函式宣告倒過來寫,將原來的第一句放在第二句的位置,就不行了,因為編譯器是從上往下編譯的,原來的第二句並沒有給b先賦值。

二、inline行內函數
1、在呼叫點把程式碼直接展開(不會給sum函式開闢棧,而是直接的替換。)
例:

inline int
sum(int a,int b) { return a+b; } int main() { int ret = sum(10,20);//將此句直接轉換為:ret = 10+20 }

2、巨集(#define)和行內函數(inline)的區別
巨集:處於預編譯階段,不會做的詞法解析以及型別檢查,出錯的可能性高,不安全。
下面舉一個例子:

#define Max(a,b) a>b?a:b

int main()
{
    int a = Max(10,20)+20;
    printf("%d\n",a);
    return 0;
}

上面這段程式碼輸出的會是什麼呢?
輸出的是:40
為什麼呢?
上面說過了,巨集定義就是字元替換,所以原函式中的a = Max(10,20)+20就變為了a = 10>20?10:(20+20)。

行內函數:處於編譯階段,有進行詞法解析以及型別檢查,只要有錯誤就會編譯失敗,所以說inline函式是安全的。

3、行內函數和普通函式的區別
普通函式:需要開闢棧幀,清理棧幀(棧的回退)。
行內函數:沒有棧幀的開闢及回退。

4、行內函數和static函式的區別
static函式:生成符號、符號屬性;需要開闢棧幀,清理棧幀(棧的回退)。
行內函數:不產生符號;沒有棧幀的開闢及回退。

static和inline函式的相同點:都只能在當前檔案中使用。

另:普通函式與static函式的區別:普通函式中的變數是global全域性變數,static函式中的是local區域性變數。

Q:我們在什麼時候用行內函數呢?
A:在棧幀開闢的開銷(函式呼叫的開銷)>函式執行的開銷時。

最後需要注意的是:1、inline函式只在release版本中生效,在debug版本中的inline函式的呼叫也需要棧幀的開闢和回退。
2、inline函式只是對編譯器的一個建議,最終能不能起作用是由編譯器來決定的。比如:inline函式對遞迴函式不起作用,因為不知道需要展開函式幾次。

三、函式的過載
c語言:函式產生符號由函式名稱決定,所以函式的定義不能重名,否則出現重定義。

C++:函式產生符號由函式名稱+形參的型別+形參的個數決定。函式定義可以重名,但前提是函式的引數不能完全相同。
例如:

bool compare(int a,int b)
{
    cout<<"compare(int,int)"<<endl;
    return a>b;
}

bool compare(double a,double b)
{
    cout<<"compare(double,double)"<<endl;
    return a>b;
}

bool compare(char* a,char* b)
{
    cout<<"compare(char*,char*)"<<endl;
    return strcmp(a,b)>0?true:false;
}

int main()
{
    compare(10,20);
    compare(10.5,20.6);
    compare("hello","world");
    return 0;
}

上方的函式在C語言中不能編譯成功,因為函式重定義了。
而在C++中,三個函式名稱分別為:compare_int_int;compare_double_double;compare_char*_char*

所以,1、我們把函式名相同,引數列表不同的函式稱為一組過載函式。2、過載必須處於同一個作用域中。例:如果在主函式中加入一行函式宣告

int main()
{
    compare(int a,int b);
    compare(10,20);
    compare(10.5,20.6);
    compare("hello","world");
    return 0;
}

再編譯時就會出現型別無法轉換的情況。

如果將以上程式碼改為

bool compare(int a,int b)
{
    cout<<"compare(int,int)"<<endl;
    return a>b;
}

bool compare(float a,float b)
{
    cout<<"compare(float,float)"<<endl;
    return a>b;
}

bool compare(char* a,char* b)
{
    cout<<"compare(char*,char*)"<<endl;
    return strcmp(a,b)>0?true:false;
}

int main()
{
    compare(10,20);
    compare(10.5,20.6);
    compare("hello","world");
    return 0;
}

編譯時就會出錯,不知將10.5和20.6轉換為int還是double型別。
這裡寫圖片描述
箭頭所指的線路都是編譯器預設會選擇的優先轉向。
比如short型別參與運算時,都會預設轉換成int型計算,最後再將結果轉化為short,這樣做是為了提高精度。

為了更好的理解,下面舉出一個例子:

unsigned int a = 1;
char b = -1;
char c = a>b?'a':'b';//b
cout<<c<<endl;

unsigned short a = 1;
char b = -1;
char c = a>b?'a':'b';//a
cout<<c<<endl;

四、C與C++的相互呼叫
1、C++轉C(看得見原始碼)
這裡寫圖片描述
2.1、C轉C++
這裡寫圖片描述
2.2、C轉C++(看不見原始碼)
這裡寫圖片描述