return 的傳值、傳參考


在 定義函式時,一定要定義函式的傳回值型態,如果函式不傳回值,則使用void表示不傳回任何數值;一旦指定函式的傳回值不為void,則在函式中一定要 使用return傳回一個數值,否則編譯器將回報錯誤。

在之前的範例中,您只是使用傳值的方式傳回函式的執行結果,事實上您也可以傳回一個指標或是參考,傳回指標通常意味著您要對這個指標所指向的記憶體位置作 取值或更動的動作,例如下面的程式中,您在函式中動態建立一個陣列,並傳回它的指標值:

#include <iostream> 
using namespace std;

int* createArray(int);
void deleteArray(int*);

int main() {
int m = 0;

cout << "陣列大小: ";
cin >> m;

int *arr = createArray(m);

for(int i = 0; i < m; i++) {
arr[i] = i;
}

for(int i = 0; i < m; i++) {
cout << "arr[" << i << "] = "
<< arr[i] << endl;
}

deleteArray(arr);

return 0;
}

// 傳回建立的陣列位址
int* createArray(int m) {
int *a = new int[m];

for(int i = 0; i < m; i++) {
a[i] = 0;
}

return a;
}

void deleteArray(int* arr) {
delete [] arr;
}

執行結果:

陣列大小: 5
arr[0] = 0
arr[1] = 1
arr[2] = 2
arr[3] = 3
arr[4] = 4


由於您使用動態配置的方式,所以在使用delete之前,這塊被配置的記憶體並不會自動清除,所以您可以直接傳回 給呼叫函式的主函式,注意如果您不是使用 new來配置,則在副函式中所宣告的變數記憶體,在函式執行結束後都會自動消失,則您傳回指標值也就沒有意義,也會造成存取錯誤,因為該塊記憶體在副函式 執行完畢後已經自動回收了。

在這邊的範例您也看到了如何傳遞陣列給函式,以及如何傳回一個陣列,在C++中傳遞陣列或傳回陣列一律使用傳遞記憶體位址的方法,因為陣列名稱本身就是個 指標,儲存有 位址資訊。

在進一步討論傳參考之前,您先要瞭解到,在C++中傳遞一個物件,預設也是使用傳值的方式,例如下面這個程式:

#include <iostream> 
#include <string>
using namespace std;

string foo();

int main() {
string str; // 空字串

str = foo();

cout << "address: " << &str
<< endl << str << endl;

return 0;
}

string foo() {
string s = "This is caterpillar speaking.";

cout << "address: " << &s
<< endl << s << endl;

return s;
}

執行結果:

address: 0x22ff40
This is caterpillar speaking.
address: 0x22ff50
This is caterpillar speaking.


您可以發現到,兩個字串的記憶體位置並不相同,表示傳回的字串在傳值時是複製一份新的傳回,您可以改用傳參考的方 式將字串值傳回,方式如下所示:

#include <iostream> 
#include <string>
using namespace std;

string& foo();

int main() {
string &str = foo();

cout << "address: " << &str
<< endl << str << endl;

delete &str;

return 0;
}

string& foo() {
string *s = new string("This is caterpillar speaking.");

cout << "address: " << s
<< endl << *s << endl;

return *s;
}

執行結 果:
address: 0x3d2458
This is caterpillar speaking.
address: 0x3d2458
This is caterpillar speaking.


使用傳參考的方式是直接告知物件的記憶體位置,而沒有作物件複製的動作,可以加快程式執行的效率,而某些情況下, 編譯器也會自行將物件的傳回結果自動設定 為傳參考,即使語法上並沒有指定,例如:

#include <iostream> 
#include <string>
using namespace std;

string foo();

int main() {
string str = foo();

cout << "address: " << &str
<< endl << str << endl;

str = foo(); // 指定新字串,會複製一份

cout << "address: " << &str
<< endl << str << endl;

return 0;
}

string foo() {
string s = "This is caterpillar speaking.";

cout << "address: " << &s
<< endl << s << endl;

return s;
}

執行結 果:
address: 0x22ff50
This is caterpillar speaking.
address: 0x22ff50
This is caterpillar speaking.
address: 0x22ff40
This is caterpillar speaking.
address: 0x22ff50
This is caterpillar speaking.


在這個程式中,第一行宣告str並指定foo()傳回值時,雖然並沒有指定傳回參考,但是您可以看到兩個字串變數 的值都指向同一塊記憶體,但是編譯器會自 動判斷並以傳參考的方式來傳回這個物件,而不是使用傳值的方法傳回,這是編譯器的功能,您可以將之當作一個效率上的考量作法,但不可以將之當作一個傳參考 的方式來使用。

必須注意的是,函式中的區域變數在函式開始時被配置,在函式結束後所佔有的記憶體位址也會被清除,絕對不要傳回一個區域變數的位址給呼叫者,或是以傳參考 的方式傳回一個區域變數,因為您所存取的記憶體位址中資料是未知的,所以結果是不可預期的。