Przekazywanie parametru przez wartość i referencję
twonek
Co jakiś czas na forum pojawia się problem wynikający z niezrozumienia mechanizmu przekazywania parametrów w C++. Więc zamiast po raz kolejny wysilić się na jakiś mądry przykład postanowiłem napisać raz artykuł, a potem tylko dawać linki do niego.
Standardowo parametry funkcji (pod tym pojęciem rozumiem również metody w klasach) są przekazywane przez wartość. Czyli funkcja operuje na kopii zmiennej która została do niej przekazana.
#include <iostream>
using namespace std;
void foo(int x)
{
x = 34;
}
int main()
{
int x = -1;
foo(x);
cout << x << endl;
}
Z tego powodu powyższy kod wypisuje -1
, a nie 34
.
Zatem co należy zrobić jeśli się chce modyfikować parametr wewnątrz funkcji? Istnieją 2 rozwiązania:
- przekazać przez referencję
- przekazać "przez wskaźnik" (napisałem to w cudzysłowie nie bez powodu, ale o tym za chwilę)
Przekazanie parametru przez referencję wymaga tylko jednej małej modyfikacji do powyższego kodu
void foo(int& x)
i voilà, wszystko działa!
Przekazanie parametru "przez wskaźnik" wymaga niestety modyfikacji w trzech miejscach, no ale znam takich, którzy wolą 3 od 1 ;)
#include <iostream>
using namespace std;
void foo(int* x) // 1
{
*x = 34; // 2
}
int main()
{
int x = -1;
foo(&x); // 3
cout << x << endl;
}
Dlaczego napisałem "przez wskaźnik" w cudzysłowie? Ano bo to jest tak naprawdę przekazywanie wskaźnika przez wartość. Czyli przekazujesz do funkcji jakiś wskaźnik, funkcja robi kopię tego wskaźnika i potem operuje na tej kopii. Popatrzmy na ten przykład:
#include <iostream>
using namespace std;
void foo(int* ptrX, int* ptrY)
{
ptrX = ptrY;
*ptrX = 111;
}
int main()
{
int x = -1;
int y = 39;
int* ptrX = &x;
int* ptrY = &y;
foo(ptrX, ptrY);
cout << *ptrX << " " << *ptrY << endl;
}
Wskaźnik ptrX
wskazuje na zmienną x
, czyli jego wartość to adres x
. Dla ustalenia uwagi załóżmy, że ten adres to 333
. Tak samo niech wartość wskaźnika ptrY
wynosi 678
. Wewnątrz funkcji foo()
mamy
ptrX = ptrY;
teraz wartość ptrX
wynosi 678
*ptrX = 111;
przypisuje do zmiennej pod adresem 678
wartość 111. A zmienna pod adresem 678
to właśnie y
.
Ale pamiętajmy, że wskaźniki były przekazywane przez wartość, więc po wyjściu z foo()
ptrX
nadal ma wartość 333
. Dlatego
cout << *ptrX << " " << *ptrY << endl;
wypisuje zmienne pod adresami 333
i 678
, czyli x
i y
.
Zrozumienie tego mechanizmu pozwala uniknąć częstych błędów tego typu
void addNode(list* head)
{
head = new list;
...
}
head
jest przekazywany przez wartość, więc można zmienić wartość zmiennej pod adresem, na który wskazuje head
, ale nie można zmienić wartości samego wskaźnika head
. Rozwiązania w tym przypadku są takie same jak dla zwykłej zmiennej:
- przekazać wskaźnik przez referencję
void addNode(list*& head)
- przekazać adres wskaźnika
head
void addNode(list** head)
{
*head = new list;
...
}
przy czym trzeba oczywiście pamiętać o zmianie w miejscu wywołania tej funkcji (patrz: modyfikacja nr 3 na początku artykułu).
Swoją drogą w tym przypadku można też rozwiązać problem w ten sposób:
list* addNode(list* head)
{
head = new list;
....
return head;
}