【轉】C++函數——指針和引用類型的參數傳遞

普通的函數形參:

void fun(int v1,int v2)
{
   cout<<v1<<"\t"<<v2<<endl;
}

這就是一個最基本的帶有兩個普通形參的void型函數,在函數的()中定義的兩個變量就是函數的形參。在調用函數時,編譯器用實參對形參初始化並運行程序,需要指出的是形參只是實參的副本,這一過程中形參的可以重新賦值,而實參的值不會相應發生變化,可以舉個例子:

void fun(int v1,int v2)
{
   int num=v1;v1=v2;v2=num;
   cout<<v1<<"\t"<<v2<<endl;
}
int main()
{
   int num1=10;int num2=20;
   fun(num1,num2);
   cout<<num1<<"\t"<<num2<<endl;
   return 0;
}

運行結果:

20       10

10       20

這個例子證明了對形參的重新賦值不會改變實參的值,這就是最基本的函數參數傳遞,這裏不再做詳細討論,下面我想說的是指針的形參是指針類型和引用類型的情況。

一.指針形參的傳遞(非引用形參的傳遞)

函數的參數可以是指針,它與普通類型的形參一樣,形參的任何改變只作用於形參局部副本,也就說是當對形參重新賦值時,函數的實參不會發生變化

void fun(int *p,int *q)
{
   cout<<*p<<"\t"<<*q<<endl;
   int num=*p;
   *p=*q;*q=num;
   cout<<*p<<"\t"<<*q<<endl;
   p=0;q=0;
   cout<<p<<"\t"<<q<<endl;
}
int main()
{
   int num1=10;int num2=20;
   int *ptr1=&num1;int *ptr2=&num2;
   cout<<num1<<"\t"<<num2<<endl;
   cout<<ptr1<<"\t"<<ptr2<<endl;
   fun(ptr1,ptr2);
   cout<<num1<<"\t"<<num2<<endl;
   cout<<ptr1<<"\t"<<ptr2<<endl;
   return 0;
}

運行結果:

10    20
0x22ff74       0x22ff70
10    20
0      0
20    10
0x22ff74       0x22ff70

   這是在我機器上運行的結果,當然了,你運行的是時候可以能得到的地址值是不一樣的,因爲他在內存中分配的空間不同,得到的結果當然不會相同,不過這個不關鍵。我們來看,由於函數爲指針類型的形參,爲了容易理解,我們在調用函數之前先輸出num1,num2以及指針ptr1,ptr2的值以便比較。我們來看函數部分,我通過中間量交換了*p與*q的值並輸出他們兩個值,然後又將兩個形參賦值爲0,並輸出其值,然後調用函數之後,我又輸出了num1,num2以及實參ptr1,ptr2的值。

   從運行結果中我們發現,與普通形參調用不同的是num1與num2的值發生了變化,相同的是實參沒有變化,而且形參的一切操作都沒有改變實參的值(函數中我將形參重新賦值了,形參發生了變化,但從運行結果中可以看出,實參沒有發生變化)。

   我們來總結一下,調用形參爲指針類型的函數時,實參必須是指針(或者是取地址操作後的對象如&num1,&num2),在編譯時,函數的形參用主調函數所給的實參初始化。以本例解釋也就是說,編譯時系統將p初始化爲ptr1的副本,它內部存儲的地址與ptr1存儲的地址相同,也就是說*p與*ptr1指向的是同一個對象,因此當對*p和*q交換值時實際上是交換了它們所指向的對象的值,也就是交換了num1和num2的值。繼而我將p和q賦值爲0,目的是改變形參,輸出他們的值以便與比較。然後在調用完函數之後,我在主調函數中輸出了實參ptr1和ptr2的值,以及num1和num2的值,通過運行結果,我們就發現了我們的推斷都是正確的!那就是:任何對非引用形參的操作都不會改變實參的值,即使是指針類型的形參!

   值得提出的是,指針形參的操作固然有好處,但如果我們不想改變實參所指對象的值,那又該怎麼辦呢?這時我們需要const關鍵字。我們先這樣,你可以將上面程序的函數形參定義爲const類型運行並看報錯,你會發現*p=*q;*q=num;操作是不合法的,這是因爲const關鍵字的作用!大家都知道,當定義指針指向const類型對象或者定義const類型對象的引用時需要在指針前加const,即const int a=1;const int *p=aconst int& b=a;但如果我們這樣int a=1;const int *p=a; const int& b=a;會產生什麼結果呢?(大家知道的是const int *與int *const 是不同的,這裏不做詳細解釋,可以參考本blog知識點文章——“學習標準C++Primer過程中發現的幾個需要注意的問題”)首先const int *p=a; const int& b=a;此操作使得程序不能通過*p或者b來修改a的值(當然了,a的值仍然是可以改變的,例如直接賦值或者通過其他指針或者引用),根據這一現象,我們在函數的形參上加const,就確保了函數中所有改變指針所指對象值的操作都是非法的,這樣就實現了這一功能。

可以參考如下程序:

void fun(const int *p,const int *q)
{
   cout<<*p<<"\t"<<*q<<endl;
   p=0;q=0;
   cout<<p<<"\t"<<q<<endl;
}
int main()
{
   int num1=10;int num2=20;
   int *ptr1=&num1;int *ptr2=&num2;
   fun(ptr1,ptr2);
   cout<<ptr1<<"\t"<<ptr2<<endl;
   return 0;
}

運行結果:
10    20
0     0
0x22ff74     0x22ff70

二.引用參數的傳遞

1.普通類型的引用參數

還是先解釋下什麼是引用參數吧!這裏我們還是沿用上篇文章的例子吧!

oid fun(int& a,int &b)
{
   cout<<"這是實參傳遞給a b的值 \t<<"<<a<<">>\t<<"<<b<<">>"<<endl;
   int num=a;
   a=b;b=num;
   cout<<"這是函數操作後的a b值 \t<<"<<a<<">>\t<<"<<b<<">>"<<endl;
}
int main()
{
   int num1=10;int num2=20;
   cout<<"這是調用前的num1和num2\t<<"<<num1<<">>\t<<"<<num2<<">>"<<endl;
   fun(num1,num2);
   cout<<"這是調用後的num1和num2\t<<"<<num1<<">>\t<<"<<num2<<">>"<<endl;
   return 0;
}

運行結果:

這是調用前的num1和num2 <<10>>    <<20>>

這是實參傳遞給a b的值     <<10>>    <<20>>

這是函數操作後的a b值     <<20>>    <<10>>

這是調用後的num1和num2 <<20>>    <<10>>

這是一個簡單的函數形參爲引用的典型例子。我先解釋一下這個函數的運行,與前面不同的是我加了漢字解釋,這樣更容易區分,爲了方便比較結果我先打印了數值num1和num2的值,然後調用函數,先打印實參傳遞過來的值,直接操作參數,對其值進行交換,然後再輸出操作後參數的值,最後在主調函數中打印調用函數後實參的值,這是程序的運行過程,我們比較結果不難發現,引用形參的函數與普通形參或者指針形參函數的不同在於函數引用形參調用後,它修改了實參的值!!!

2.const類型的引用參數

有時候程序並不需要它本有的特性,有時候我們的代碼並不需要修改實參的值,這時候難道就不能用引用類型的的形參嗎?其實到這裏都能想到了,正如指針所具有的特性一樣,指針類型形參的特點是能改變其所指對象的值,而我們加了const就改變這點,那麼在這裏加const有沒有同樣的效果呢??我們不妨用個例子來說明一下。

void fun(const int& a,const int &b)
{
   cout<<"這是實參傳遞給a b的值 \t<<"<<a<<">>\t<<"<<b<<">>"<<endl;
   int num=a;
   a=b;b=num;
   cout<<"這是函數操作後的a b值 \t<<"<<a<<">>\t<<"<<b<<">>"<<endl;
}
int main()
{
   int num1=10;int num2=20;
   cout<<"這是調用前的num1和num2\t<<"<<num1<<">>\t<<"<<num2<<">>"<<endl;
   fun(num1,num2);
   cout<<"這是調用前的num1和num2\t<<"<<num1<<">>\t<<"<<num2<<">>"<<endl;
   return 0;
}

系統報錯:

lab.cpp: In function `void fun(const int&, const int&)':

lab.cpp:10: error: assignment of read-only reference `a'

lab.cpp:10: error: assignment of read-only reference `b'

這個例子我只把上面程序函數的形參加了const關鍵字,我們看一下報錯啊,先聲明一下啊,我英語四級成績剛夠線,六級也不準備考了,我的意思是說編程工具報錯的英語雖然不容易看懂,但是當你多次編譯的時候就能纔出它的意思了,我們來猜一下,第一行,在函數void fun(const int&, const int&)'中,第二行:錯誤,a是隻讀類型的指針。第三行:b是隻讀類型的指針。這並不是說加了const之後a和b的值都不能改變了,這是值得提出的,const關鍵字在此處的作用是把引用定義爲只讀的,也就是說,引用所指向的對象的值不能改變!但是由於引用只能在定義時初始化,a和b的值也是不能改變的,但這不是const的作用。

最後 我覺得有必要提出的是:傳遞指向指針的引用。

void fun(int* &a,int* &b)
{
   cout<<"這是實參傳遞給a b的值 \t<<"<<a<<">>\t<<"<<b<<">>"<<endl;
   cout<<"這是實參傳遞給*a*b的值\t<<"<<*a<<">>\t<<"<<*b<<">>"<<endl;
   int *num=a;
   a=b;b=num;
   cout<<"這是函數操作後的a b值 \t<<"<<a<<">>\t<<"<<b<<">>"<<endl;
   cout<<"這是函數操作後的*a*b值\t<<"<<*a<<">>\t<<"<<*b<<">>"<<endl;
}
int main()
{
   int num1=10;int num2=20;
   int *ptr1=&num1;int *ptr2=&num2;
   cout<<"這是調用前的num1和num2\t<<"<<num1<<">>\t<<"<<num2<<">>"<<endl;
   fun(ptr1,ptr2);
   cout<<"這是調用前的num1和num2\t<<"<<num1<<">>\t<<"<<num2<<">>"<<endl;
   return 0;
}

這裏我不再對程序作解釋,大家可以按上面的方式理解一下程序,我想告訴大家的是這雖然是形參的一種特殊形式,但這完全可以看作是對指針對象的引用,也就是說把指針當做實參傳遞給函數,函數中通過對形參的換值,把實參指針存儲的地址做了交換,從而交換了指針所指的空間,繼而*ptr1*ptr2的值發生了變化,而num1和num2的值沒有變化!!

注:後面的內容作者表達的有點亂,但總體來說值得看看,理清了一些關係。當然,函數部分還有很多值得我們探討的內容,可以自己再進行總結。

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