C++編程入門系列之十(函數的參數傳遞與內聯函數)

C++編程入門系列之十(函數的參數傳遞與內聯函數)

上一講雞啄米講了函數的定義和調用,這一講接着給大家講函數的相關知識。讓大家瞭解編程入門時需要知道的函數的參數是怎樣傳遞的以及內聯函數的概念。

一.函數的參數傳遞

上一講中雞啄米提到了函數的參數有形參和實參兩種,那兩者到底有什麼區別呢?我們在聲明及定義函數的時候跟的參數叫做形參,調用函數時傳進去的參數稱爲實參。比如,函數int fun(int a),這裏的a就是形參,我們在其他地方調用函數fun,將外部的變量b傳進去,即fun(b),這裏的b就是實參。實參可以是常量、變量或表達式,它的類型必須和形參相符。函數沒有被調用時,形參並不佔用內存,只有在調用時纔會分配內存空間,然後將實參傳進去。函數參數的傳遞有兩種方式,值調用和引用調用。

1.值調用

值調用就是調用函數時,給形參分配內存空間,將實參拷貝給形參,之後的函數執行中形參和實參就脫離了關係,誰都不影響誰。也就是值調用時,只是用實參的值初始化下形參,之後兩者互不影響。

雞啄米給大家個例子就很明白了:

    #include <iostream>       
    using namespace std;
    void Swap(int a, int b);
    int _tmain(int argc, _TCHAR* argv[])
    {
               int x=5, y=10;
               cout<<"x="<<x<<"    y="<<y<<endl;
               Swap(x,y);
               cout<<"x="<<x<<"    y="<<y<<endl;
               return 0;
    }
    void Swap(int a, int b)
    {
               int t;
               t=a;
               a=b;
               b=t;
    }

   運行結果是:
    x=5      y=10
    x=5      y=10

從運行結果中,大家可以看出,本來我們想調用Swap函數把x和y的值交換過來,但是事與願違。這就是因爲,這是值調用的方式,x和y分別傳值給a和b後,a跟b的值在Swap內怎樣變化都影響不到x和y,所以兩次輸出x和y的值沒有變化。

2.引用調用

如果我們想讓被調函數中對形參做的修改同樣對主調函數中的實參有效,用值調用不行,那怎麼辦呢?答案是用引用調用。

引用是一種特殊類型的變量,我們可以認爲它是另一個變量的別名,利用引用名和原來的變量名訪問變量的效果是一樣的。引用的形式是:類型標識符 &引用名。比如:

   int  i, j; 
   int &ri=i;    // 建立一個int型的引用ri,並將其初始化爲變量i的一個別名
   j=10;
   ri=j;          // 相當於 i=j;

使用引用雞啄米提醒大家必須注意以下問題:聲明一個引用時,必須同時對它進行初始化,使它指向一個已存在的對象;一旦一個引用被初始化後,就不能改爲指向其它對象。簡單說就是引用定義的時候就指定它指向的變量,之後就不能變了。

引用可以作爲形參,比如void swap(int& a, int& b) {…}。這個時候引用就不需要對其初始化了,因爲形參只是類型說明,主調函數調用這個函數時纔會爲形參分配內存,也纔會用實參來初始化形參。用引用調用後,形參就是實參的別名而已,對形參做的任何改變都會影響實參發生同樣的改變。雞啄米再拿上面那個例子改成引用調用說明下:

   #include <iostream>      
   using namespace std;
   void Swap(int& a, int& b);
   int _tmain(int argc, _TCHAR* argv[])
   { 
             int x=5, y=10;
             cout<<"x="<<x<<"    y="<<y<<endl;
             Swap(x,y);
             cout<<"x="<<x<<"    y="<<y<<endl;
             return 0;
   }
   void Swap(int& a, int& b)
   { 
             int t;
             t=a;
             a=b;
             b=t;
   }
   運行結果就跟我們想的一樣了:
   x=5      y=10
   x=10      y=5

引用調用後x和y的值成功的交換了。值調用和引用調用的區別只是函數的形參寫法不同,主調函數調用被調函數時的調用語句是一樣的。

二.帶默認形參值的函數

函數在聲明時可以預先定義默認的形參值。調用時若給出實參則用實參初始化形參,如果沒有給出實參則採用預先定義的默認形參值。例如:

   int add(int x=5,int y=6)      // 定義默認形參值
   { 
             return x+y; 
   }
   int main()
   {    
            add(10,20);              // 用實參來初始化形參,實現10+20
            add(10);                 // 形參x採用實參值10,y採用默認值6,實現10+6
            add();                   // x和y都採用默認值,分別爲5和6,實現5+6
            return 0;
   }

默認參數值必須按照從右向左的順序定義。在有默認值的形參右面,不能出現無默認值的形參。也就是說應該把有默認值的形參都一塊放到右邊,不能讓有默認值的跟沒默認值的形參穿插着放。因爲在調用時,實參初始化形參是按從左向右的順序。比如:

   int add(int x,int y=5,int z=6);   //正確
   int add(int x=1,int y=5,int z);   //錯誤
   int add(int x=1,int y,int z=6);   //錯誤

調用出現在函數體實現之前時,默認形參值必須在函數原型中也就是聲明時給出;而當調用出現在函數體實現之後時,默認形參值需在函數實現時給出。例如:

調用在實現前時:

   int add(int x=5,int y=6);
   int main()
   {   
           add(); //調用在實現前
           return 0;
   }
   int add(int x,int y)
   {   
           return x+y;   
   }

調用在實現後時:

   int add(int x=5,int y=6)
   {   
           return  x+y;   
   }
   int main()
   {   
           add(); //調用在實現後
           return 0;
   }

在相同的作用域內,默認形參值的說明應保持惟一,但如果在不同的作用域內,允許說明不同的默認形參。這裏的作用域是指直接包含函數原型說明的大括號所界定的範圍,具體雞啄米會在後面跟大家講。例:

    int add(int x=1,int y=2);
    int main()
    { 
               int add(int x=3,int y=4);
               add();//使用局部默認形參值(實現3+4)
               return 0;
    }
    void fun()
    {   ...
              add();//使用全局默認形參值(實現1+2)
    }

三.內聯函數

函數雖然有很多優點,比如代碼複用,便於維護等。但是調用函數時需要保存現場和返回地址,被調函數執行完後還要取出這些值繼續執行,這些過程在時間和空間方面都有開銷。對於一些規模小、功能簡單的函數可以定義成內聯函數。內聯函數在調用時不需要那些轉移帶來的開銷,它是在編譯的時候把函數體代碼嵌入到所有調用它的語句處,我們可以認爲直接把函數體的代碼放那裏了,當然也不完全一樣,畢竟它可能有參數。

內聯函數定義時使用關鍵字inline,語法形式如下:

   inline  類型標識符  被調函數名(含類型說明的形參表)

雞啄米提醒大家使用內聯函數時必須注意:內聯函數體內不能有循環語句和switch語句;內聯函數的定義必須出現在內聯函數第一次被調用之前;對內聯函數不能進行異常接口聲明,就是不能聲明可能拋出的異常,異常接口聲明雞啄米以後會講到。

內聯函數應該是語句比較少、結構比較簡單的函數,不應該是複雜的函數,因爲它對編譯器來說就是代碼,如果很複雜會造成代碼膨脹,反而增大開銷,這種情況下其實多數編譯器就都會自動把它作爲普通函數來處理了。一般來說,包含循環語句的函數一定不能定義成內聯函數。雞啄米再給大家一個內聯函數的例子:

    #include <iostream>       
    using namespace std;
    inline double CalArea(double radius)
    { 
                return 3.14*radius*radius;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
               double r(3.0);
               double area;
               area=CalArea(r);
               cout<<area<<endl;
               return 0;
    }
   運行結果是:28.26

這一講雞啄米給大家的源代碼的例子比較多,大家看看這些代碼就會更好的理解雞啄米所講的內容,希望大家在編程入門的時候不要着急,好好消化教程。

思考

  1. 什麼是值傳遞,什麼是引用傳遞,各自具體的形式是什麼?
  2. 內聯函數的形式?需要注意什麼?

轉自雞啄米

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