函數何時值傳遞,何時指針,何時引用傳遞總結

編程中定義函數,一般三種傳遞方法,看是簡單,想靈活合理選擇,還需要大量的編程經驗和技巧。 故在這裏特意總結一下這三種傳遞的方法。

根本區別: 函數值傳遞不改變變量值,如果想改變變量值,需要返回值,然後用變量接收一下這個返回值。  而指針傳遞和引用傳遞都可以在函數內改變變量值,不需要通過返回值的形式改變。

應用場合: 當想通過這個函數,改變好幾個變量的值,多個變量都通過函數返回值來改變變量值方式費時費力。所以這種場合就比較適合使用指針和引用。

指針傳遞需要開內存,如果忘記釋放的話,可能導致內存泄露。浪費大量內存。所以引用傳遞更好一些。

引用傳遞的性質象指針傳遞,書寫形式象值傳遞,

理由:如果只需要借用一下別名,就沒必要用指針,.

void changeVar(int &myVar,int newValue);  // 引用傳遞

這個函數第一個輸入的參數不是一個指針,它是那個將被傳遞給這個函數的原始變量的一個別名。在changeVar()函數裏對這個參數變量進行的任何操作都將反映在changeVar()函數外的那個原始變量身上。這意味着changeVar()函數與原來的一樣。

void changeVar(int myVar,int newValue){  //值傳遞,沒有返回值,起不到改變變量的作用

  myVar=newValue;

}

這使得這個函數更容易被調用----只需要提供一個變量名;

int main(){

int myNum=20;

changeVar(myNum,90);  // 引用和值傳遞寫法一樣, 可見引用傳遞簡單,這也是c++新增加的功能,表現了c++的強大

}

這比值傳參語法上更簡單了。

引用傳遞”方式把參數值傳遞給一個函數是C++的新增功能,這可以讓函數的調用語法更加簡單清晰。

提示:1.在定義函數時,還可以讓它以引用傳遞方式而不是以值傳遞方式返回: int &myFuntion();

           2.除了可以改變有關變量的值,引用傳遞方式的另一個好處是它的開銷相對要小一些:因爲不需要在函數裏創建臨時變量來容納那些值,程序的內存佔用量當然會小一些。

           3.如果想獲得引用傳遞方式帶來的性能改善,但不想改變某個變量的值,可以把相應的輸入參數定義爲一個常量:

 void myFunc(const int &myNum);

通過這樣定義的函數,可以把具體的參數直接傳遞給它:myFunc(7);


 

int script_orientation(VOID *dic, unsigned char *im, int w, int h,
                               RECT roi, int &script_id,  // NOLINT
                               int &orient_id,  // NOLINT
                               float &conf) {  // NOLINT
函數調用
 
 const char* model_file = "model.txt";
 void* model = CNN_Init(model_file);
cv::Mat image = cv::imread(path, 0);
    int script_id, orient_id;

script_orientation(model, image.data, image.cols, image.rows, roi,
                               script_id, orient_id, confidence);
這裏需要script_id, orient_id 兩個返回值, 所以這兩個定義爲應用較好。 調用時就不需要再去定義變量接收 這兩個 script_id, orient_id 兩個返回值


下面寫一個函數,從函數中把我需要的兩個值傳出來,由於傳出來的值比較多,所以不考慮用return來返回,需要通過參數把修改後的值拉出來供我使用,很當然的就想到了用指針,但是值就是傳不出來;使我對原有的大腦中指針的思維產生混沌感,今天一上午才把函數傳遞又走了一遍,才明白其中道理(道行還是很淺),現在整理如下:

    我之前寫的方法部分代碼,相信好多人都是這麼寫的:
    
[cpp] view plain copy
  1. BOOL GetStartEndBoxes(BOOL bRow, const SwCrsrShell& rShell,  SwTableBox *pStt,  SwTableBox *pEnd)  
  2. {  
  3.     SwSelBoxes aBoxes;  
  4.     ... ...  
  5.   
  6.     pStt = aBoxes[0];  
  7.     pEnd = aBoxes[aBoxes.Count() - 1];  
  8.     return TRUE;  
  9. }  
  10. 調用:  
  11.        。。。 。。。      SwTableBox *pStt=0;  
  12.       SwTableBox *pEnd=0;  
  13.   
  14.      if ( !GetStartEndBoxes(bRow, *this, pStt, pEnd) )  
  15.     return FALSE;  

    傳遞一個指針到調用函數,希望獲得更新後的值(pStt, pEnd),爲什麼沒有獲得到呢?疑問

    概念

   首先從概念上來說一下這幾種函數傳參方式及區別:
   1、值傳遞:形參是實參的拷貝,改變函數形參的值並不會影響外部實參的值,這是最常用的一種傳參方法,也是最簡單的一種傳參方法,只需要傳遞參數,返回值那是return考慮的;
   2、指針傳遞:指針傳遞參數從本質上來說也是值傳遞,它傳遞的是一個地址。【值傳遞過程中,被調函數的形參作爲被調函數的局部變量來處理,即在函數內的棧中開闢內存空間以存放由主調函數放進來的實參的值,從而成了實參的一個副本(記住這個,函數內參數的是實參的副本)】。由於指針傳遞的是外部實參的地址,當被調函數的形參值發生改變時,自然外部實參值也發生改變。
   3、引用傳遞:被調函數的形參雖然也作爲局部變量在棧中開闢了內存空間,但是棧中存放的是由主調函數放進的實參變量的地址。被調函數對形參的任何操作都被處理成間接尋址,即通過棧中存放的地址訪問主調函數中實參變量(實參和形參通過引用,合二爲一,說白點就是:一個人,有兩個名字那種;後面想會詳細說)。因此,形參的任何改動都會直接影響到實參。

   實例    

先從簡單的例子開始:
  1.  值傳遞:例子略過。  
  2. 指針傳遞:
[cpp] view plain copy
  1. void swap(int *a,int *b)  
  2. {  
  3.     int temp;  
  4.     temp=*a;  
  5.     *a=*b;  
  6.     *b=temp;  
  7.     cout<<"a=" <<a<<" ,"<<"b="<<b<<endl;  
  8.     cout<<"*a=" <<*a<<" ,"<<"*b="<<*b<<endl;  
  9.     cout<<"&a=" <<&a<<" ,"<<"&b="<<&b<<endl;  
  10. }  
(剛上大學的時候就接觸過的例子,交換值)調用:
[cpp] view plain copy
  1. int main(){  
  2.     int x=1;  
  3.     int y=2;  
  4.     cout<<"x=" <<x<<" ,"<<"y="<<y<<endl;  
  5.     cout<<"&x=" <<&x<<" ,"<<"&y="<<&y<<endl;  
  6.     swap(&x,&y);  
  7. }  
一定要記住這種調用方式
[cpp] view plain copy
  1. swap(&x,&y);  
如指針傳遞的概念上所述,傳地址給形參。
形如:int *a = &x;//用於指針傳遞,a有自己獨立的內存地址,存儲的內容是x的地址,*a是存x的值。
輸出結果:

傳入值的各變量的初始狀態(地址狀態):
               
上圖關係可以知道:a(b)是一個指向外部實參地址的指針,*a(*b)是指針的內容,如果改變了*a(*b)也必然導致外部實參的改變

交換後:
       *a=2,*b=1;
       
       
這樣的結果是由於a或者b指針指向x或者y的地址的緣故,因此由於*a,*b值得交換導致外部實參發生變化。

 
        思考一下,下面的操作能否實現值得變化?
        簡單測試代碼:
       
[cpp] view plain copy
  1. int change(char* name){  
  2.     cout<<"*******CHANGE--BEFORE******"<<endl;  
  3.     cout<<"name=" <<name<<endl;  
  4.     cout<<"*name=" <<&name<<endl;  
  5.     name="alter";  
  6.     cout<<"*******CHANGE--AFTER********"<<endl;  
  7.     cout<<"name=" <<name<<endl;  
  8.     cout<<"*name=" <<&name<<endl;  
  9.     return 1;  
  10. }  
  11. int main()  
  12. {  
  13.     char *str = "this is a test";  
  14.   
  15.     cout<<"******MAIN--BEFORE*****"<<endl;  
  16.     cout<<"str=" <<str<<endl;  
  17.     cout<<"*str=" <<&str<<endl;  
  18.     change(str);  
  19.     cout<<"*****MAIN--AFTER*****"<<endl;  
  20.     cout<<"str=" <<str<<endl;  
  21.     cout<<"*str=" <<&str<<endl;  
  22.     return 1;  
  23. }  
    執行結果:(打印的輸出的時候,有點錯誤,*str應該爲 &str)
    從結果中發現,並未達到改變值得效果,爲什麼?這個測試代碼和本文開始的疑問是一樣的,那就進一步分析:
    
    傳入值的各變量的初始狀態(地址狀態):
     
   執行賦值操作
[cpp] view plain copy
  1. name="alter";  
    系統首先需要給字符串“alter”分配內存空間(地址),然後指針才指向其地址。
       
 所以*str並沒有發生變化,因此最後打印出來的仍是“this is a test”,這也解釋了我開始時的迷惑!微笑
另一種成功傳遞參數的指針調用方法----指針的指針:
  
[cpp] view plain copy
  1. void my_malloc(void** p, int size)  
  2. {  
  3.     *p = malloc(sizeof(int)*size);  
  4. }  
  5. int main()  
  6. {  
  7.     int *a;  
  8.     my_malloc(&a , 10);  
  9.     return 1;  
  10. }  
執行結果:(有些參數沒有用,只是爲了打印出來看看)
當我們沒有執行到給*p分配空間的時候:
執行malloc(size) 後的圖如下
 
   賦值給*p後:由於p指向&a即a的地址,*p則指向a的地址裏的值,現在又要把分配的內存指向*p,所以,a的值即爲新分配的內存!(這個比較難轉圈)
   
    
    然後,我們就給指針a 分配內存成功了。
     3、引用傳遞:
     
[cpp] view plain copy
  1. void swapref(int &a,int &b)  
  2. {  
  3.     cout << "******************before swapref:******************"<<endl;  
  4.     cout<<"a=" <<a<<" ,"<<"b="<<b<<endl;  
  5.     cout<<"&a=" <<&a<<" ,"<<"&b="<<&b<<endl;  
  6.     int temp;  
  7.     temp=a;  
  8.     a=b;  
  9.     b=temp;  
  10.     cout << "******************after swapref:******************"<<endl;  
  11.     cout<<"a=" <<a<<" ,"<<"b="<<b<<endl;  
  12.     cout<<"&a=" <<&a<<" ,"<<"&b="<<&b<<endl;  
  13. }  
  14. int main(){  
  15.     int x=1;  
  16.     int y=2;  
  17.     cout<<"******MAIN--BEFORE*****"<<endl;  
  18.     cout<<"x=" <<x<<" ,"<<"y="<<y<<endl;  
  19.     cout<<"&x=" <<&x<<" ,"<<"&y="<<&y<<endl;  
  20.     //swap(&x,&y);  
  21.     swapref(x, y);  
  22.     cout<<"*****MAIN--AFTER*****"<<endl;  
  23.     cout<<"x=" <<x<<" ,"<<"y="<<y<<endl;  
  24.     cout<<"&x=" <<&x<<" ,"<<"&y="<<&y<<endl;  
  25. }  
一定要記住這種調用方式
[cpp] view plain copy
  1. swapref(x, y);  
形如:int &a=x; //用於引用傳遞,可以理解爲a就是x,x就是a,只不過名字不一樣
執行結果:
    這個具體就不分析了,記住引用傳遞實參和形參是一樣的,只是名字不同而已。


總結:
       本文重點還是在參數傳指針方面,指針確實令人頭疼,今天遇到了,會出錯,弄明白以後,等過段時間,又忘了,又遇到錯誤,再來看,這樣不斷反覆,希望能不斷的提升,對指針的認識不斷的加深!

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