Q: 參數是如何傳遞到function的?
A: 在C++當中有二種方式,一種是by value,一種是by reference。By value表示被呼叫者的stack會產生一份原參數的複製品,任何在此複製品上的動作都不會影響到原本function的參數。而by reference代表東西的位置會被傳遞(reference握有某個東西位置,但它表現得像是它自己就是那東西),所以被呼叫者可以直接更動最原始被傳進來的那個東西。
Q: 那麼所謂的"pass by pointer"是什麼?
A: 沒有這種東西,這是一種錯誤的概念。一個指標就是握有某個記憶體位置。你可以傳遞一個指標變數到一個function中,但這個指標變數是"pass by value"的。你可以簡單的說,如果一個function prototype的參數是有'&'的,那麼它就是pass by reference,否則就是pass by value。
Q: 我可以看些例子嗎?
A: 可以,下面是幾個例子:
第一個例子是一個整數由pass by value傳遞到function foo()中,並且將它們變大。由於一份複製品會產生,所以變大不會影響原本的變數,所以程式會輸出0 1 0。
void foo(int i)
{
i++;
cout << i << endl;
}
int main()
{
int i = 0;
cout << i << endl;
foo(i);
cout << i << endl;
return 0;
}
由於pass by value需要建立一個複製品,所以當你在傳遞東西時,如果它是個class或是struct的話,它們必須提供copy constructor才行,因為這樣才能讓function去做複製品。
下面這個例子在foo() function中使用了pass by reference,它可以真的去改變原始的參數,所以它會輸出 0 1 1 。
void foo(int &i)
{
i++;
cout << i << endl;
}
int main()
{
int i = 0;
cout << i << endl;
foo(i);
cout << i << endl;
return 0;
}
你也可以將一個指向整數的指標(即一個位置)傳遞到foo()中。指標的傳遞是pass by value (這代表如果你在foo()當中改變pointer的話不會影響在main當中的pointer值),但是由於這複製品亦是main當中數值的位置,所以我們可以直接改變該數。這個程式也會輸出0 1 1。
void foo(int *i)
{
(*i)++;
cout << *i << endl;
}
int main()
{
int i = 0;
cout << i << endl;
foo(&i);
cout << i << endl;
return 0;
}
當function以指標為參數時,有可能發生別人將NULL pointer傳進來的情況(各種理由都有可能),所以你最好逼免掉這樣的bug。
假設,你想要傳遞一個指標某個buffer的指標,並且希望該function(重新)為它配置記憶體空間,並且將此buffer填滿,就像這樣:
// 這程式是"錯的"
void foo(byte *buffer)
{
// delete the previous buffer
if(buffer != NULL)
delete [] buffer;
// allocate new memory
buffer = new byte[2];
// fill the memory
buffer[0] = 0;
buffer[1] = 1;
}
int main()
{
byte * buffer = NULL;
foo(buffer);
delete [] buffer;
return 0;
}
這將會造成錯誤,就如同前面所解釋的,由於傳遞指標是pass by value,一份指標複製品會產生,當function return回到main之後,main當中的指標仍舊會是指向原本的位置,和呼叫foo()之前一樣 (在這個例子中就是NULL)。要做到你想做的事,你必須在傳遞指標時 (即buffer的起始位置),使用pass by referene。
// 這是修正後正確的程式
void foo(byte* &buffer)
{
// delete the previous buffer
if(buffer != NULL)
delete [] buffer;
// allocate new memory
buffer = new byte[2];
// fill the memory
buffer[0] = 0;
buffer[1] = 1;
}
int main()
{
byte * buffer = NULL;
foo(buffer);
delete [] buffer;
return 0;
}
這可以保證在foo()中更改指向byte陣列的指標,可以真的更動到main當中的指標值。