C/C++三種函數傳參方式以及指針與引用小結

(1)傳值,就是把你的變量的值傳遞給函數的形式參數,實際就是用變量的值來新生成一個形式參數,因而在函數裏對形參的改變不會影響到函數外的變量的值。
(2)傳址,就是傳變量的地址賦給函數裏形式參數的指針,使指針指向真實的變量的地址,因爲對指針所指地址的內容的改變能反映到函數外,也就是能改變函數外的變量的值。
(3)傳引用,實際是通過指針來實現的,能達到使用的效果如傳址,可是使用方式如傳值。
說幾點建議:如果傳值的話,會生成新的對象,花費時間和空間,而在退出函數的時候,又會銷燬該對象,花費時間和空間。
因而如果int,char等固有類型,而是你自己定義的類或結構等,都建議傳指針或引用,因爲他們不會創建新的對象。


例1:下面這段代碼的輸出結果爲:
#include<stdio.h>
void change(int*a, int&b, int c)
{
      c=*a;
      b=30;
      *a=20;
}
int main ( )
{
      int a=10, b=20, c=30;
      change(&a,b,c);
      printf(“%d,%d,%d,”,a,b,c);
      return 0;
 }
結果:20  30  30


解析:
該題考察函數傳參問題。
1,指針傳參 -> 將變量的地址直接傳入函數,函數中可以對其值進行修改。
2,引用傳參 -> 將變量的引用傳入函數,效果和指針相同,同樣函數中可以對其值進行修改。
3,值傳參   -> 在傳參過程中,首先將c的值複製給函數c變量,然後在函數中修改的即是函數的c變量,然後函數返回時,系統自動釋放變量c。而對main函數的c沒有影響。


例2:
#include<stdio.h>  
void myswap(int x, int y)  
{  
    int t;  
    t=x;  
    x=y;  
    y=t;  
}  
int main()  
{  
    int a, b;  
    printf("請輸入待交換的兩個整數:");  
    scanf("%d %d", &a, &b);  
    myswap(a,b);  //作爲對比,直接交換兩個整數,顯然不行  
    printf("調用交換函數後的結果是:%d 和 %d\n", a, b);  
    return 0;  
}  
 
 
#include<stdio.h>  
void myswap(int *p1, int *p2)  
{  
    int  t;  
    t=*p1;  
    *p1=*p2;  
    *p2=t;  
}  
int main()  
{  
    int a, b;  
    printf("請輸入待交換的兩個整數:");  
    scanf("%d %d", &a, &b);  
    myswap(&a,&b);  //交換兩個整數的地址  
    printf("調用交換函數後的結果是:%d 和 %d\n", a, b);  
    return 0;  
}  
 
#include<stdio.h>  
void myswap(int &x, int &y)  
{  
    int t;  
    t=x;  
    x=y;  
    y=t;  
}  
  
int main()  
{  
    int a, b;  
    printf("請輸入待交換的兩個整數:");  
    scanf("%d %d", &a, &b);  
    myswap(a,b);  //直接以變量a和b作爲實參交換  
    printf("調用交換函數後的結果是:%d 和 %d\n", a, b);  
    return 0;  
}  
第一個的運行結果:輸入2 3,輸出2 3 
第二個的運行結果:輸入2 3,輸出3 2
第三個的運行結果:輸入2 3,輸出3 2

解析:
在第一個程序中,傳值不成功的原因是指在形參上改變了數值,沒有在實參上改變數值。
在第二個程序中,傳地址成功的原因利用指針改變了原來的地址,所以實參就交換了。
在第三個程序中,引用是直接改變兩個實參變量a,b的值,所以就交換了。

////////////////////////////////////////////////////////////////////////////


下文會通過例子詳細說明關於值傳遞,指針傳遞,引用傳遞 

  1)值傳遞:

    形參是實參的拷貝,改變形參的值並不會影響外部實參的值。從被調用函數的角度來說,值傳遞是單向的(實參->形參),參數的值只能傳入,

不能傳出。當函數內部需要修改參數,並且不希望這個改變影響調用者時,採用值傳遞。

  2)指針傳遞:

    形參爲指向實參地址的指針,當對形參的指向操作時,就相當於對實參本身進行的操作

  3)引用傳遞:

    形參相當於是實參的“別名”,對形參的操作其實就是對實參的操作,在引用傳遞過程中,被調函數的形式參數雖然也作爲局部變量在棧中開闢了內存空間,但是這時存放的是由主調函數放進來的實參變量的地址。被調函數對形參的任何操作都被處理成間接尋址,即通過棧中存放的地址訪問主調函數中的實參變量。正因爲如此,被調函數對形參做的任何操作都影響了主調函數中的實參變量。

    下面的代碼對此作出了細緻解釋(從實參,形參在內存中存放地址的角度 說明了問題的本質,容易理解  )

 1 #include<iostream>
 2 using namespace std;
 3 //值傳遞
 4  void change1(int n){
 5     cout<<"值傳遞--函數操作地址"<<&n<<endl;         //顯示的是拷貝的地址而不是源地址 
 6     n++;
 7 }
 8 
 9 //引用傳遞
10 void change2(int & n){
11     cout<<"引用傳遞--函數操作地址"<<&n<<endl; 
12     n++;
13 }
14  //指針傳遞
15 void change3(int *n){
16      cout<<"指針傳遞--函數操作地址 "<<n<<endl; 
17     *n=*n+1;
18  } 
19 int     main(){
20     int n=10;
21     cout<<"實參的地址"<<&n<<endl;
22     change1(n);
23     cout<<"after change1() n="<<n<<endl;
24     change2(n);
25     cout<<"after change2() n="<<n<<endl;
26     change3(&n);
27     cout<<"after change3() n="<<n<<endl;
28     return true;
29 }
運行結果如下,(不同的機器可能會有所差別)

可以看出,實參的地址爲0x22ff44

    採用值傳遞的時候,函數操作的地址是0x22ff20並不是實參本身,所以對它進行操作並不能改變實參的值

再看引用傳遞,操作地址就是實參地址 ,只是相當於實參的一個別名,對它的操作就是對實參的操作
接下來是指針傳遞,也可發現操作地址是實參地址
    那麼,引用傳遞和指針傳遞有什麼區別嗎?

  引用的規則: 
引用被創建的同時必須被初始化(指針則可以在任何時候被初始化)。 

不能有NULL引用,引用必須與合法的存儲單元關聯(指針則可以是NULL)。 
一旦引用被初始化,就不能改變引用的關係(指針則可以隨時改變所指的對象)。 
  指針傳遞的實質:

    指針傳遞參數本質上是值傳遞的方式,它所傳遞的是一個地址值。值傳遞過程中,被調函數的形式參數作爲被調函數的局部變量處理,即在棧中開闢了內存空間以存放由主調函數放進來的實參的值,從而成爲了實參的一個副本。值傳遞的特點是被調函數對形式參數的

    任何操作都是作爲局部變量進行,不會影響主調函數的實參變量的值。(這裏是在說實參指針本身的地址值不會變)如果理解不了大可跳過這段

  指針傳遞和引用傳遞一般適用於:

    函數內部修改參數並且希望改動影響調用者。對比指針/引用傳遞可以將改變由形參“傳給”實參(實際上就是直接在實參的內存上修改,不像值傳遞將實參的值拷貝到另外的內存地址中才修改)。

    另外一種用法是:當一個函數實際需要返回多個值,而只能顯式返回一個值時,可以將另外需要返回的變量以指針/引用傳遞給函數,這樣在函數內部修改並且返回後,調用者可以拿到被修改過後的變量,也相當於一個隱式的返回值傳遞吧。

    以下是我覺得關於指針和引用寫得很不錯的文章,大家可參照看一下,原文出處地址:http://xinklabi.iteye.com/blog/653643 

    從概念上講。指針從本質上講就是存放變量地址的一個變量,在邏輯上是獨立的,它可以被改變,包括其所指向的地址的改變和其指向的地址中所存放的數據的改變。

    而引用是一個別名,它在邏輯上不是獨立的,它的存在具有依附性,所以引用必須在一開始就被初始化,而且其引用的對象在其整個生命週期中是不能被改變的(自始至終只能依附於同一個變量)。

    在C++中,指針和引用經常用於函數的參數傳遞,然而,指針傳遞參數和引用傳遞參數是有本質上的不同的:

    指針傳遞參數本質上是值傳遞的方式,它所傳遞的是一個地址值。值傳遞過程中,被調函數的形式參數作爲被調函數的局部變量處理,即在棧中開闢了內存空間以存放由主調函數放進來的實參的值,從而成爲了實參的一個副本。值傳遞的特點是被調函數對形式參數的任何操作都是作爲局部變量進行,不會影響主調函數的實參變量的值。(這裏是在說實參指針本身的地址值不會變)

而在引用傳遞過程中,被調函數的形式參數雖然也作爲局部變量在棧中開闢了內存空間,但是這時存放的是由主調函數放進來的實參變量的地址。被調函數對形參的任何操作都被處理成間接尋址,即通過棧中存放的地址訪問主調函數中的實參變量。正因爲如此,被調函數對形參做的任何操作都影響了主調函數中的實參變量。

    引用傳遞和指針傳遞是不同的,雖然它們都是在被調函數棧空間上的一個局部變量,但是任何對於引用參數的處理都會通過一個間接尋址的方式操作到主調函數中的相關變量。而對於指針傳遞的參數,如果改變被調函數中的指針地址,它將影響不到主調函數的相關變量。如果想通過指針參數傳遞來改變主調函數中的相關變量,那就得使用指向指針的指針,或者指針引用。

爲了進一步加深大家對指針和引用的區別,下面我從編譯的角度來闡述它們之間的區別:

程序在編譯時分別將指針和引用添加到符號表上,符號表上記錄的是變量名及變量所對應地址。指針變量在符號表上對應的地址值爲指針變量的地址值,而引用在符號表上對應的地址值爲引用對象的地址值。符號表生成後就不會再改,因此指針可以改變其指向的對象(指針變量中的值可以改),而引用對象則不能修改。

最後,總結一下指針和引用的相同點和不同點:

  1)相同點:

都是地址的概念;
指針指向一塊內存,它的內容是所指內存的地址;而引用則是某塊內存的別名。

  2)不同點:

指針是一個實體,而引用僅是個別名;
引用只能在定義時被初始化一次,之後不可變;指針可變;引用“從一而終”,指針可以“見異思遷”;
引用沒有const,指針有const,const的指針不可變;(具體指沒有int& const a這種形式,而const int& a是有     的,  前者指引用本身即別名不可以改變,這是當然的,所以不需要這種形式,後者指引用所指的值不可以改變)
引用不能爲空,指針可以爲空;
“sizeof 引用”得到的是所指向的變量(對象)的大小,而“sizeof 指針”得到的是指針本身的大小;
指針和引用的自增(++)運算意義不一樣;
引用是類型安全的,而指針不是 (引用比指針多了類型檢查)

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