Nawet z samego przekazywania parametrów do funkcji można zrobić temat na artykuł 😊. A temat jest bardzo ważny! Przekazywanie przez referencję to w języku C jedna z fundamentalnych operacji jakie trzeba znać, aby sobie ułatwić życie, a w niektórych przypadkach po prostu wykonać pomyślnie jakieś zadanie 🤯! Cóż to takiego? Jak to działa? Na co pozwala? Czytaj, a się dowiesz 😄!
PRZEKAZYWANIE PRZEZ REFERENCJĘ W JĘZYKU C WYJAŚNIONE KROK PO KROKU
Stoimy przed jednym z najważniejszych zagadnień jakie w języku C trzeba bezsprzecznie znać! Wyjaśniam najpierw teorię, a potem praktykę!
POJĘCIE REFERENCJI
Musimy zacząć od słowa "referencja". W programowaniu, oznacza to odniesienie się do konkretnego miejsca w pamięci poprzez wskaźnik. A konkretniej, przez jego adres w systemie szesnastkowym ℹ️. Patrząc od niskopoziomowej architektury, technicznie to jedyny sposób na posiadanie 100% pewności, że modyfikacja wartości danej zmiennej zostanie utrwalona. Więcej na ten moment nie trzeba pisać 😊.
KIEDY TRZEBA ZASTOSOWAĆ PRZEKAZYWANIE PRZEZ REFERENCJĘ?
Wiemy co to, to teraz kiedy👍. Kiedy przekazywanie przez referencję jest konieczne 🤔? Wtedy, gdy w funkcji niezwracającej wartości, chcemy utrwalić zmiany w zmiennej, lecz uwaga - typu prostego ⚠️. Typem prostym określamy takie rodzaje danych, jak 👇:
- liczba całkowita (int),
- liczba zmiennoprzecinkowa (float/double),
- znak (char),
- wartość logiczna (bool) - przy czym w języku C sama z siebie nie występuje (trzeba skorzystać z dyrektywy "stdbool.h").
To są rodzaje danych, które nie korzystają ze wskaźnika ℹ️. To oznacza, że wprowadzając je jako parametry funkcji, wszelkie zmiany wartości w obrębie tej funkcji...po prostu "wyparują" (nie będą obowiązywały na zewnątrz funkcji, która zmodyfikowała wartość) 💨!
DLACZEGO TRZEBA ZASTOSOWAĆ PRZEKAZYWANIE PRZEZ REFERENCJĘ?
Wiemy co to, wiemy kiedy, to teraz dlaczego 😅. Typy proste przekazywane w miejsce parametru danej funkcji są kopiami i ta sytuacja obrazuje operację nazywaną "przekazywaniem przez kopię", co jak się domyślasz, jest przeciwieństwem opisywanego "rodzaju" przekazywania 🙂. Właśnie dlatego samo wstawienie zmiennej w miejsce parametru do funkcji modyfikującej wartość 👇:
void multiplyBy(float x, float multiplier)
{
x *= multiplier;
}nic Ci nie da i wartość pozostanie taka sama po wyjściu z tej funkcji 😐. Aby otrzymać zmienioną wartość bez używania referencji, jedynym sposobem jest zwrócenie wartości przez funkcję:
int getNumberMultipliedBy(int x, int multiplier)
{
return x*multiplier;
}i przypisanie wyniku do zmiennej:
number = getNumberMultipliedBy(number, 2);co w takiej sytuacji jest "okrężną drogą" do uzyskania tego samego, choć z formalnego punktu widzenia jest jak najbardziej poprawne 😊. Ponadto, ograniczamy się do zwracania jednej wartości co wywołanie 👎.
PRZYKŁAD KODU ŹRÓDŁOWEGO
Wiemy co to, wiemy kiedy, wiemy dlaczego, to teraz jak 😁! Oto prosty przykład funkcji, która ma na celu utrwalić przemnożenie liczby zmiennoprzecinkowej przez drugą liczbę, także zmiennoprzecinkową 👇:
#include <stdio.h>
void printInt(int i);
void multiplyIntBy(int *i, int multiplier);
int main(void)
{
int i = 4;
printInt(i);
multiplyIntBy(&i, 2);
printInt(i);
return 0;
}
void printInt(int i)
{
printf("Liczba = %d\n", i);
}
void multiplyIntBy(int *i, int multiplier)
{
*i *= multiplier;
}Zanim przejdziemy do wyjaśniania istoty sprawy, pozwolę zwrócić uwagę na wstawienie prototypu funkcji nad funkcją "main". Podział funkcji osobno na prototyp i definicję jest bardzo dobrą praktyką do późniejszego wydzielania kodu na własne biblioteki (zobacz odrębny artykuł na ten temat) ℹ️.
"MAGIA" PRZEKAZYWANIA PRZEZ REFERENCJĘ
Teraz do rzeczy. Mamy definicję funkcji 👇:
void multiplyIntBy(int *i, int multiplier)przyjmującej 2 parametry i jak widzisz, pierwszy z nich nie oczekuje samej zmiennej, a wskaźnika do niej (spójrz na asterysk przed nazwą zmiennej "x") ⚠️! To jest ważne, żebyś umiał(a) to rozróżnić 🔔. Zmienna, a wskaźnik do zmiennej 👀!
Typy proste same z siebie nie są zdolne do utrwalania zmian, toteż należy je "wspomóc" 🙂. Stąd też, w miejsce wywoływania naszej funkcji wstawiamy adres do zmiennej (znak ampersandu):
multiplyIntBy(&i, 2);Przekazując adres zachowujemy odniesienie do tej samej zmiennej, czyli wspomnianą wcześniej referencję, a to z kolei umożliwia modyfikowanie tej samej zmiennej, która znajduje się na zewnątrz funkcji i zachowanie tych zmian po jej zakończeniu 🚀. Na tym polega różnica 😄!
OPERACJA "WYŁUSKANIA" (DEREFERENCJI)
Zostało wyjaśnienie samej treści funkcji. Popatrz na to 👇:
*i *= multiplier;Tutaj również mamy asterysk przed nazwą zmiennej 😲. Tylko chwila - przecież użyliśmy go w miejscu parametru funkcji, także jakie ma zastosowanie w tym momencie 🤔?
W języku C, asterysk ma kilka znaczeń ℹ️. W tym przypadku, oznacza tzw. operację "wyłuskania" wartości. Chodzi o dosłowne "wydobycie" aktualnej wartości typu prostego posługując się przekazanym adresem do zmiennej ✔️. "Wyłuskanie" jest nazywane zamiennie "dereferencją".
Opisując całą instrukcję, dochodzi do następujących kroków:
- wydobądź ("wyłuskaj") aktualną wartość zmiennej używając adresu (*i),
- przemnóż "wyłuskaną" wartość zmiennej "i" przez wartość parametru "multiplier" i wynik przypisz zmiennej "i".
Tym sposobem, uruchamiając cały powyższy program, rozpocznie się wypisaniem liczby 4, następnie wartość zostanie przemnożona przez 2 i zakończy się wypisaniem liczby 8, co kończy dowód trwałości modyfikacji przez referencję 🏆.
"OPŁACALNOŚĆ" STOSOWANIA PRZEKAZYWANIA PRZEZ REFERENCJĘ
Możesz sobie pomyśleć na co Ci to skoro możesz zwrócić wartość za pomocą słowa "return" 😊. Pojedynczą wartość tak, natomiast przekazywanie przez referencję pozwala nie tylko uniknąć zwracania wartości, lecz także modyfikować WIELE wartości pod rząd 😱!
![]() |
Przekazywanie przez referencję w języku C polega na podaniu adresu do zmiennej typu prostego w momencie wywoływania funkcji akceptującej wskaźnik do danej zmiennej, której zmiany mają zostać utrwalone po zakończeniu funkcji.
I na tym kończę przekazywanie przez referencję w języku C jako temat niniejszego materiału ✅. Solidny temat Ci "przekazałem" 😝!
