Operatory dereferencji * i adresu & to jedne z fundamentalnych narzędzi pracy z pamięcią w C. Umożliwiają bezpośrednie zarządzanie danymi w pamięci, co sprawiło, że C stał się popularnym językiem do programowania systemowego.
Historia pytania:
Od początku istnienia języka C (w latach 70.) jego filozofia była ściśle związana z niskopoziomowym zarządzaniem pamięcią. Operatory * i & realizują technikę pośredniego adresowania, wykorzystywaną na poziomie procesora, co pozwala na pracę z wskaźnikami, dynamiczne przydzielanie pamięci oraz tworzenie wydajnych struktur danych.
Problem: Błędy w użyciu tych operatorów prowadzą do licznych błędów: wycieków pamięci, uszkodzenia danych, segfaultów. Kompilator nie zawsze wyraźnie sygnalizuje te błędy, szczególnie gdy typy wskaźników mają tę samą wielkość, ale różnią się zawartością.
Rozwiązanie: Należy starannie podchodzić do typu wskaźnika, śledzić cykl życia przydzielanej pamięci, przeprowadzać inicjalizację i poprawne zwolnienie, a także sprawdzać poprawność operacji dereferencji i używanych adresów.
Przykład kodu:
int x = 10; int *p = &x; // adres int y = *p; // dereferencja (otrzymujemy wartość z adresu) // Praca z wskaźnikiem na tablicę int arr[3] = {1,2,3}; int *pa = arr; printf("%d", *(pa+1)); // drugi element tablicy
Kluczowe cechy:
Czy można wziąć adres zmiennej tymczasowej, np.: & (x + y)?
Nie, adres wyrażenia wziąć nie można, ponieważ wynik wyrażenia — nie jest obiektem pamięci. Adres można brać tylko od zmiennej, tablicy lub struktury.
Przykład kodu:
int z = 5; int p = &(z + 1); // Błąd kompilacji
Czym różni się dereferencja wskaźnika typu void?
Wskaźnika typu void * nie można dereferencjonować bezpośrednio, dopóki nie zostanie przekształcony do konkretnego typu. To uniwersalny wskaźnik, ale operacje dereferencji są typoniezależne tylko po jawnej konwersji:
void *pv = &x; int value = *(int*)pv; // OK
Czy można dereferencjonować wskaźnik zerowy (NULL)?
Nie, prowadzi to do nieokreślonego zachowania — usunięcia pamięci lub awaryjnego zakończenia. Zawsze sprawdzaj wskaźnik przed dereferencją:
int *ptr = NULL; if (ptr) { *ptr = 10; // Nigdy się nie wykona }
Programista bierze adres lokalnej zmiennej w funkcji, zwraca go, a następnie dereferencjonuje wskaźnik w kodzie wywołującym.
Zalety:
Wady:
Wykorzystywane jest dynamiczne przydzielanie pamięci dla zmiennej, adres jest zwracany kodowi wywołującemu i na końcu zwalniany przez free.
Zalety:
Wady: