1. 程式人生 > >小議C++中函式的引數的傳遞

小議C++中函式的引數的傳遞

     c++中為每一個函式都維護了一個執行棧(活動記錄),這個棧儲存了與該函式相關的一系列資訊,包括函式中宣告的變數,傳遞給函式的實際引數,以及該函式的返回地址等,使用gdb偵錯程式可以清楚的看到這一切。當呼叫發生時,該函式會將實際引數的值copy到執行棧,也就是常說的值傳遞。那麼很顯然的就是,如果我們在函式中改變的只是實際引數的值的拷貝,所以實際引數的值並沒有發生變化。

     那麼,如果我們想要去改變實際引數的值,怎麼辦呢?首先當然會想到指標啦,因為指標實在是太牛了,它可以到處指啊。如果你用熟了指標,可以說你已經是一個c語言的有點高手了。:-),最經典的一個用指標操作實際引數的例子就是交換兩個數的值,簡直是不想在把它拿出來舉例子了,網上百度一下就到處都是,更不用說是谷歌了。還有一種改變實際引數的辦法是採用引用引數。最初對引用引數不理解的時候,我就記住一點,引用引數的作用就是給實際引數起了一個別名,它繫結到實際引數上。對引用引數所做的所有的操作,都會作用到實際引數。說的稍微深奧一點就是,引用引數傳遞的實際是一個變數的左值,什麼是左值呢?顧名思義,左值就是可以被賦值的值。一個變數有兩個值,一個是左值,一個是右值。左值就是該變數的值在記憶體單元中的地址,右值就是該變數的值。體會意思就行,這一點說是話,我是不太清楚啊,悲哀。

     另外指標和引用還有一個共同的優點就是它們都可以提高程式的效率。雖然對於引數為簡單的變數的函式來說,提高的效率可能杯水車薪,微不足道。但是對於那種引數為巨大的類等的東西,當函式將實際引數的值複製到執行棧的時間代價和空間代價是任何一個真正的程式設計師所不能容忍的。採用指標和引用時,函式操作的就是實際引數,所以不用將它複製到自己的執行棧。當然,有的哥們會擔心如果採用這種方式,我們不小心將實際引數的值改變了,但實際上並不想讓實際引數的值改變怎麼辦?這個不用擔心,因為C++的設計者早就想到了這一點,他們引入了const這個關鍵字(貌似是從c語言繼承過來的,那就不是c++設計者的創意啦)。通過用const修飾的指標和引用,使得使用者不能夠通過這個指標或引用修改它們所指向的記憶體單元。

     既然指標和引用都有這兩個優點,它們兩個又有什麼區別呢?從表面上看,指標可以不用被初始化,可以為空,可以隨便亂指,當然這都是在符合語法規則的前提下。而引用必須被初始化為一個變數,並且永遠不允許再改變。另外引用有一個重要的優點就是看著“好看”,又有效率。我說的“好看”,那可是真的好看,比如說,在過載運算子中:

Matrix a,b,c;    //Matrix是個巨大的類
Matrix operator+(const Matrix &m1,const Matrix &m2)
{
      Matrix result;
      //add a and b -> result
     return result;
}

c=a+b   //是不是很好看

但是如果使用指標的話,那可真就是慘不忍睹了:
Matrix a,b,c;    //Matrix是個巨大的類
Matrix operator+(const Matrix *m1,const Matrix *m2)
{
      Matrix result;
      //add m1 and m2 -> result
     return result;
}

c=&a+&b   //是不是很慘啊
&(&a+&b)+&c //這是更慘的

引用還真是個好東西啊。

     當C++的函式傳遞的引數是個陣列時,編譯器會將其看作一個指標,也就是陣列首元素的地址。編譯器不知道陣列到底有多少個元素,其實我們最常使用的main函式就已經向我們所名了這個道理,當使用命令列引數的時候,傳遞的兩個引數分別是argc和argv,這個argc就是用來專門告訴編譯器引數個數的。連main函式都得這樣,其他函式就更不用說了。所以就算你在定義時像這樣void sort(int a【10】),但這是沒用的,編譯器只會將它看成 void sort(int* a)。要想讓函式知道陣列有幾個元素怎麼辦呢,當然上面個已經說明了一種方法。還有一種方法就是陣列引用,呵,又是引用,沒錯,可以寫成這個樣子 void sort(int (&a) [10]),就是著個樣子,看起來有點變態。但是千萬不要把那個(&a)中的括號去掉,否則就真變態了。

     就寫到這裡吧,該回去了。