Zdobądź zawód w IT!     |       Ścieżki Kariery 30% taniej     |      Jeszcze przez:

3 dni 18 godzin
close
Cart icon
User menu icon
User icon
Skontaktuj się z nami:
+48 888-916-333
Lightbulb icon
Jak to działa?
FAQ icon
FAQ
Contact icon
Kontakt
Terms of service icon
Regulamin zakupów
Privacy policy icon
Polityka prywatności
Zdjęcie główne artykułu.

Wskaźniki w języku C

Wskaźniki potrafią sprawić wiele problemów - i to nie tylko początkującym programistom. To taki element, który z jednej strony oferuje wielkie możliwości, ale jednocześnie wymaga sporej wprawy. W tym artykule poznasz najważniejsze techniki pracy ze wskaźnikami w języku C. Zaczynamy!

Naucz się programować w C: Ścieżka Kariery C Developer

Podstawowe zasady pracy ze wskaźnikami

Wskaźnikiem nazywamy zmienną, która posiada specjalną moc. Zmienna taka jest w stanie przechowywać adres zmiennej czyli miejsce gdzie przechowywana jest konkretna informacja. Możesz sobie wyobrazić, że wskaźnik to taka specjalna zakładka w Twojej książce. Aby dostać się do ulubionego rozdziału nie musisz wertować wszystkich stron. Wystarczy, że otworzysz książkę w miejscu gdzie znajduje się zakładka i gotowe.

Aby dobrze operować na wskaźnikach musisz zrozumieć współpracę dwóch specjalnych operatorów:

 & - operator adresu
 * - operator wyłuskania

Spójrz do czego może służyć operator & (czyli operator adresu):

  
  #include 
  
  
  int main() {
    int number = 123;
   
    printf("Wartość number to: %d \n", number);
    printf("Adres number to: %p \n", &number);
  
  
    return 0;
  }
  
  
  

Używając modyfikatora %p możemy wyprowadzić na ekran odpowiednio sformatowany adres. A to efekt działania programu:

  
  Wartość number to: 123
  Adres number to: 0x7fff7ff1be3c
  
  
  

Na początku deklarujemy zmienną number. Następnie wyświetlamy jej zawartość, a na końcu odwołujemy się do jej adresu przy pomocy operatora &.

Teraz kolejny przykład:

  
  #include 
  
  
  int main() {
    int number = 123;
    int *ptr; // 1
   
    ptr = &number; // 2
   
    // &
    printf("Adres number to: %p \n", &number);
    printf("Wartość number to: %d \n", number);
   
    // *
    printf("Adres number to (ptr): %p \n", ptr);
    printf("Wartość number to (*ptr): %d \n", *ptr);
   
    return 0;
  }
  
  
  

Popatrz na taką deklarację: int *ptr. Jest to wskaźnik (*) o nazwie ptr, który może wskazywać na adres zmiennej o typie int.

Teraz kolejna deklaracja: ptr = &number. Oznacza ona, że wskaźnik ptr będzie pokazywał na adres zmiennej number.

Po analizie kolejnych linii kodu możemy stwierdzić, że: ptr to jest to samo co &number, a z kolei *ptr to jest to samo co number. I to cała podstawowa filozofia użycia wskaźników. W praktyce jednak ich użycie nie będzie takie proste, jak w powyższym przykładzie. Dlatego przejdźmy od razu do kolejnych zagadnień.

Wskaźniki i tablice

Wskaźników często używa się w połączeniu z tablicami. Najpierw przypomnijmy sobie podstawy pracy z tablicami w języku C:

  
  #include 
  
  
  int main() {
  
  
    int tab[3] = { 1, 2, 3 };
   
    printf("tab[0] = %d \n", tab[0]);
    printf("tab[1] = %d \n", tab[1]);
    printf("tab[2] = %d \n", tab[2]);
  
  
    return 0;
  }
  
  
  

Mamy trójelementową tablicę liczb całkowitych. Do elementów możemy dostać się za pomocą indeksu. Na pewno pamiętasz, że pierwszy element tablicy indeksowany jest od 0. Efekt działania naszego programu wygląda tak:

  
  tab[0] = 1
  tab[1] = 2
  tab[2] = 3
  
  
  

Wyobraź sobie, że elementy w tablicy są poukładane blisko siebie. Możemy tą właściwość wykorzystać i zastosować wskaźniki. Postaramy się uzyskać identyczny efekt umiejętnie je przesuwając.

Spójrz na przykład:

  
  #include 
  
  
  int main() {
  
  
    int tab[3] = { 1, 2, 3 };
   
    int *ptr = &tab[0];
   
    printf("tab[0] = %d \n", *ptr);
    ptr++;
    printf("tab[1] = %d \n", *ptr);
    ptr++;
    printf("tab[2] = %d \n", *ptr);
  
  
    return 0;
  }
  
  
  

Efekt jest identyczny jak w poprzednim przykładzie, ale kod jest trochę inny. W pierwszej kolejności inicjalizujemy wskaźnik na adres pierwszego elementu tablicy:

  
  int *ptr = &tab[0];
  
  
  

Następnie staramy się odwołać do wartości, która kryje się pod pierwszym elementem tablicy używając operatora * (gwiazdka):

  
  printf("tab[0] = %d \n", *ptr);
  
  
  

W kolejnej linijce przesuwamy nasz wskaźnik o jedno miejsce w prawo. Dzięki takiej operacji wskazujemy na adres kolejnego elementu tablicy, czyli adres elementu kryjącego się pod indeksem 1.

  
  ptr++;
  
  
  

Następne operacje już znasz. Przeanalizuj na spokojnie kod i postaraj się zrozumieć prawdziwą moc wskaźników.

Wskaźniki do funkcji

Wskaźniki do funkcji są zaawansowanym mechanizmem w języku C, który pozwala na przechowywanie adresów funkcji i wywoływanie ich dynamicznie w czasie wykonania programu.

Zanim pokażemy ci, w jaki sposób można zadeklarować wskaźnik do funkcji, przygotujemy wstępny kod z prostymi funkcjami odpowiedzialnymi za sumowanie i odejmowanie liczb całkowitych.

  
  #include 
  
  
  void add(int a, int b) {
   printf("Add: %d \n", a + b);
  }
  
  
  void sub(int a, int b) {
   printf("Sub: %d \n", a - b);
  }
  
  
  int main(void) {
   int a = 4, b = 3;
   
   add(a, b);
   sub(a, b);
   
   return 0;
  }
  
  
  

Rezultat działania programu jest następujący:

  
  Add: 7
  Sub: 1
  
  
  

Ten fragment kodu nie powinien sprawiać ci wiele problemów. Mamy do dyspozycji dwie funkcje: add() oraz sub(). Funkcje wykonują operacje dodawania i odejmowania. Za chwilę będziemy chcieli użyć uniwersalnego wskaźnika do funkcji, który obsłuży obie te funkcje za jednym zamachem. Spójrz na poniższy przykład, który pokaże, jak wskaźniki są potężne:

  
  #include 
  
  
  void add(int a, int b) {
   printf("Add: %d \n", a + b);
  }
  
  
  void sub(int a, int b) {
   printf("Sub: %d \n", a - b);
  }
  
  
  int main(void) {
   int a = 4, b = 3;
   
   void (* operation) (int, int);
   
   operation = add;
   operation(a, b);
   
   operation = sub;
   operation(a, b);
   
   return 0;
  }
  
  
  

Na pierwszy rzut oka kod wydaje się bardzo skomplikowany. Tak naprawdę, kluczowa jest tutaj tylko jedna linijka. Przeanalizujmy deklarację wskaźnika do funkcji i rozłóżmy go na czynniki pierwsze:

  
  void (* operation) (int, int);
  
  
  

Oto opis wskaźnika operation o typie void (* operation) (int, int)

  • void oznacza, że wskaźnik wskazuje na funkcję, która nie zwraca żadnej wartości,
  • nazwa * operation to nazwa wskaźnika (nie przywiązuj się do niej, nazwę wskaźnika ustala programista - w tym konkretnym przypadku nazwaliśmy ten wskaźnik operation),
  • (int, int) oznacza, że funkcja, na którą wskazuje wskaźnik, przyjmuje dwa argumenty typu int.

W skrócie, void (* operation) (int, int) to deklaracja wskaźnika do funkcji, która nie zwraca wartości i przyjmuje dwa argumenty typu int. Możemy użyć takiego wskaźnika do przechowywania adresu konkretnej funkcji i późniejszego wywołania tej funkcji przy jego pomocy.

Jak już mamy zadeklarowany taki wskaźnik to szukamy funkcji, która jest podobna do jego deklaracji. Akurat funkcje sub() i add() spełniają opisane warunki. Obie funkcje nie zwracają wartości (void) oraz przyjmują dwa parametry typu int. Ostatecznie możemy taki wskaźnik ustawić na wybraną funkcję:

  
  operation = add;
  
  
  

Nazwa funkcji jest jej adresem w pamięci, więc możemy taki wskaźnik przypisać do jej nazwy. Teraz nic nie stoi na przeszkodzie, aby wywołać taką funkcją posługując się wskaźnikiem:

  
  operation(a, b);
  
  
  

Wywołanie funkcji za pomocą wskaźnika jest analogiczne ze standardowym wywołaniem funkcji. Zwróć uwagę, że nasz wskaźnik można w każdej chwili “przepiąć” na inną funkcję pasującą do deklaracji typu wskaźnika.

  
  operation = sub;
  
  
  

O to tyle, jeśli chodzi o podstawy użycia wskaźników do funkcji. To bardzo interesujący mechanizm.

Naucz się programować w C: Ścieżka Kariery C Developer

Podsumowanie

Wskaźniki w języku C to obszerny temat. Możemy ich używać do pracy z tablicami, funkcjami i nie tylko. W artykule pokazaliśmy tylko część dostępnych możliwości. Jeśli chcesz opanować wskaźniki i wiele innych, praktycznych technik pracy z językiem C, to rozpocznij Ścieżkę Kariery C Developer. Z tą ścieżką poznasz język C od postaw do poziomu średniozaawansowanego. Ścieżka kończy się Egzaminem, który uprawnia cię do posługiwania się Certyfikatem Specjalisty.