programowanieProgramista systemowy

Czy możesz szczegółowo opisać mechanizmy przekazywania parametrów w funkcjach języka C: przez wartość i przez wskaźnik? Podaj przykłady, kiedy użycie każdego z podejść jest uzasadnione.

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

W języku C parametry w funkcji zawsze są przekazywane przez wartość — to znaczy, kopia wartości z wywołującego kodu jest przekazywana do funkcji. Jeśli konieczne jest zmienienie wartości zmiennej poza funkcją, używa się przekazywania przez wskaźnik.

Przekazywanie przez wartość

Podczas przekazywania skalaru (np. int) funkcja otrzymuje swoją kopię:

void foo(int a) { a = 10; } int main() { int x = 5; foo(x); // x == 5, nie zmieni się! }

Przekazywanie przez wskaźnik

Aby zmienić wartość zmiennej, używa się wskaźnika:

void foo(int* a) { *a = 10; } int main() { int x = 5; foo(&x); // x == 10, wartość się zmieniła! }

Kiedy używać wskaźnika

  • Trzeba zwrócić kilka wartości.
  • Zmienić duże struktury (oszczędność czasu i pamięci, brak kopiowania).
  • Do pracy z tablicami (zawsze są przekazywane jako wskaźnik).

Pytanie z pułapką.

Czy tablica jest parametrem funkcji przekazywanym przez referencję?

Wielu odpowiada, że "przez referencję", jednak w C tablica w sygnaturze funkcji nie może być przekazana przez referencję, w rzeczywistości degraduje do wskaźnika.

Poprawna odpowiedź:

Gdy tablica jest przekazywana do funkcji, w rzeczywistości przekazywany jest wskaźnik na jej pierwszy element. To znaczy, wywoływana funkcja nie zna rzeczywistego rozmiaru tablicy, a wszelkie zmiany elementów tablicy są odzwierciedlane w oryginalnej tablicy.

void foo(int arr[]) { arr[0] = 100; } int main() { int a[3] = {1,2,3}; foo(a); // a[0] będzie 100! }

Historia


W jednym projekcie funkcja aktualizowała wartości tablicy, zadeklarowanej jako int arr[10], ale w kodzie wywołującym tablica miała mniejszy rozmiar. Z powodu tego, że funkcja nie znała faktycznego rozmiaru, wystąpiło przepełnienie bufora i uszkodzenie pamięci.


W innym przypadku, z powodu przekazania struktury przez wartość do funkcji, cały duży blok pamięci był kopiowany kilka razy, co spowodowało spadek wydajności aplikacji.


Programista oczekiwał, że przekazanie skalaru przez "referencję" (przez wskaźnik) zapewni niezmienność wartości początkowej, ale przez pomyłkę zmienił przez wskaźnik pamięć poza zakresem (błąd w arytmetyce wskaźników), co doprowadziło do nieprzewidywalnych wyników.