1. 程式人生 > >C++中函式引數傳遞(值傳遞、指標傳遞,引用傳遞)

C++中函式引數傳遞(值傳遞、指標傳遞,引用傳遞)

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

BOOL GetStartEndBoxes(BOOL bRow, const SwCrsrShell& rShell,  SwTableBox *pStt,  SwTableBox *pEnd)
{
    SwSelBoxes aBoxes;
    ... ...
pStt = aBoxes[0]; pEnd = aBoxes[aBoxes.Count() - 1]; return TRUE; } 呼叫: 。。。 。。。 SwTableBox *pStt=0; SwTableBox *pEnd=0; if ( !GetStartEndBoxes(bRow, *this, pStt, pEnd) ) return FALSE;

傳遞一個指標到呼叫函式,希望獲得更新後的值(pStt, pEnd),為什麼沒有獲得到呢?

概念

首先從概念上來說一下這幾種函式傳參方式及區別:

  • 1、值傳遞:形參是實參的拷貝,改變函式形參的值並不會影響外部實參的值,這是最常用的一種傳參方法,也是最簡單的一種傳參方法,只需要傳遞引數,返回值那是return考慮的;

  • 2、指標傳遞:指標傳遞引數從本質上來說也是值傳遞,它傳遞的是一個地址。【值傳遞過程中,被調函式的形參作為被調函式的區域性變數來處理,即在函式內的棧中開闢記憶體空間以存放由主調函式放進來的實參的值,從而成了實參的一個副本(記住這個,函式內參數的是實參的副本)】。由於指標傳遞的是外部實參的地址,當被調函式的形參值發生改變時,自然外部實參值也發生改變。

  • 3、引用傳遞:被調函式的形參雖然也作為區域性變數在棧中開闢了記憶體空間,但是棧中存放的是由主調函式放進的實參變數的地址。被調函式對形參的任何操作都被處理成間接定址,即通過棧中存放的地址訪問主調函式中實參變數(實參和形參通過引用,合二為一,說白點就是:一個人,有兩個名字那種;後面想會詳細說)。因此,形參的任何改動都會直接影響到實參。

例項

一、值傳遞(略過)

二、指標傳遞:

void swap(int *a,int *b)
{
    int temp;
    temp=*a;
    *a=*b;
    *b=temp;
    cout<<"a=" <<a<<" ,"<<"b="<<b<<endl;
    cout<<"*a=" <<*a<<" ,"<<"*b="<<*b<<endl;
    cout<<"&a=" <<&a<<" ,"<<"&b="<<&b<<endl;
}

呼叫:

int main(){  
    int x=1;  
    int y=2;  
    cout<<"x=" <<x<<" ,"<<"y="<<y<<endl;  
    cout<<"&x=" <<&x<<" ,"<<"&y="<<&y<<endl;  
    swap(&x,&y);  
}  

一定要記住這種方法:

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值得交換導致外部實參發生變化。

思考一下,下面的操作能否實現值得變化?

測試程式碼:

int change(char* name){
    cout<<"*******CHANGE--BEFORE******"<<endl;
    cout<<"name=" <<name<<endl;
    cout<<"*name=" <<&name<<endl;
    name="alter";
    cout<<"*******CHANGE--AFTER********"<<endl;
    cout<<"name=" <<name<<endl;
    cout<<"*name=" <<&name<<endl;
    return 1;
}
int main()
{
    char *str = "this is a test";

    cout<<"******MAIN--BEFORE*****"<<endl;
    cout<<"str=" <<str<<endl;
    cout<<"*str=" <<&str<<endl;
    change(str);
    cout<<"*****MAIN--AFTER*****"<<endl;
    cout<<"str=" <<str<<endl;
    cout<<"*str=" <<&str<<endl;
    return 1;
}

結果(列印的輸出的時候,有點錯誤,*str應該為 &str):

結果

從結果中發現,並未達到改變值得效果,為什麼?這個測試程式碼和本文開始的疑問是一樣的,那就進一步分析:
傳入值的各變數的初始狀態(地址狀態):

地址

name=”alter”;

系統首先需要給字串“alter”分配記憶體空間(地址),然後指標才指向其地址。

地址

所以*str並沒有發生變化,因此最後打印出來的仍是“this is a test”,這也解釋了我開始時的迷惑!

另一種成功傳遞引數的指標呼叫方法—-指標的指標:

void my_malloc(void** p, int size)
{
    *p = malloc(sizeof(int)*size);
}
int main()
{
    int *a;
    my_malloc(&a , 10);
    return 1;
}

結果(有些引數沒有用,只是為了打印出來看看):

結果

當我們沒有執行到給*p分配空間的時候:

1

2

賦值給*p後:由於p指向&a即a的地址,*p則指向a的地址裡的值,現在又要把分配的記憶體指向*p,所以,a的值即為新分配的記憶體!(這個比較難轉圈)

3

然後,我們就給指標a 分配記憶體成功了。

三、引用傳遞:

void swapref(int &a,int &b)
{
    cout << "******************before swapref:******************"<<endl;
    cout<<"a=" <<a<<" ,"<<"b="<<b<<endl;
    cout<<"&a=" <<&a<<" ,"<<"&b="<<&b<<endl;
    int temp;
    temp=a;
    a=b;
    b=temp;
    cout << "******************after swapref:******************"<<endl;
    cout<<"a=" <<a<<" ,"<<"b="<<b<<endl;
    cout<<"&a=" <<&a<<" ,"<<"&b="<<&b<<endl;
}
int main(){
    int x=1;
    int y=2;
    cout<<"******MAIN--BEFORE*****"<<endl;
    cout<<"x=" <<x<<" ,"<<"y="<<y<<endl;
    cout<<"&x=" <<&x<<" ,"<<"&y="<<&y<<endl;
    //swap(&x,&y);
    swapref(x, y);
    cout<<"*****MAIN--AFTER*****"<<endl;
    cout<<"x=" <<x<<" ,"<<"y="<<y<<endl;
    cout<<"&x=" <<&x<<" ,"<<"&y="<<&y<<endl;
}

一定要記住這種呼叫方式

swapref(x, y);  

形如:int &a=x; //用於引用傳遞,可以理解為a就是x,x就是a,只不過名字不一樣

結果:

結果

這個具體就不分析了,記住引用傳遞實參和形參是一樣的,只是名字不同而已。

總結:

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