Wskaźniki w języku C | Codenga (2024)

Wskaźniki w języku C | Codenga (1)

Autor: Codenga 15.05.2023

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.

Wskaźniki w języku C | Codenga (2024)

References

Top Articles
Latest Posts
Article information

Author: Rubie Ullrich

Last Updated:

Views: 5776

Rating: 4.1 / 5 (52 voted)

Reviews: 91% of readers found this page helpful

Author information

Name: Rubie Ullrich

Birthday: 1998-02-02

Address: 743 Stoltenberg Center, Genovevaville, NJ 59925-3119

Phone: +2202978377583

Job: Administration Engineer

Hobby: Surfing, Sailing, Listening to music, Web surfing, Kitesurfing, Geocaching, Backpacking

Introduction: My name is Rubie Ullrich, I am a enthusiastic, perfect, tender, vivacious, talented, famous, delightful person who loves writing and wants to share my knowledge and understanding with you.