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:

  1. przekazać przez referencję
  2. 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:

  1. przekazać wskaźnik przez referencję
void addNode(list*& head)
  1. 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;
}

0 komentarzy