Czy warto używać wskaźników?

Mam takie pytanie: Czy używanie wskaźników zamiast “normalnych” zmiennych powoduje, że program działa szybciej i stabilniej?

Ja powiem tak. Wskaźniki na pewno są pomocne w życiu programisty. Bardzo są przydatne w klasach :slight_smile: Ale czy program działa szybciej i stabilniej, to muszę się przyznać, że nie wiem :stuck_out_tongue: :smiley: No i dzięki wskaźnikom ja sobie często umilam monotonne tablice (chociaż to może nie potrzebne xD). Nie pisze tab[1] tylko se ustawiam wskaźnik na element i jest mi lepiej. To oczywiście nic nie daje, ale mi to umila pisanie :slight_smile:

Zdecydowanie nie - we wskaźnikach nie są przechowywane wartości zmiennych, a adresy komórek pamięci, w których te wartości się znajdują. W związku z tym operacje na zmiennych pokazywanych wskaźnikami zajmują więcej czasu (komputer musi “udać się” do adresu pokazywanego przez wskaźnik i dopiero wtedy operować na odpowiedniej wartości). Wskaźniki mają jednak wiele innych zastosowań, które powodują, że są one w niektórych sytuacjach właściwie niezastąpione. Oto niektóre z nich:

  • rezerwacja tablic dynamicznych (operatorem new)

  • wysyłanie do funkcji jako argumentu wskaźnika zamiast całej tablicy (przez co zaoszczędza się pamięć i czas)

  • konkretne zastosowania wskaźników do funkcji, jak np. przy funkcji set_new_handler w bibliotece new.h

  • no i oczywiście wskaźnik this używany w klasach

To są tylko przykłady, innych zastosowań nie mogłem sobie w tej chwili przypomnieć :slight_smile:

Dodałbym jeszcze możliwość budowania struktur danych: list, kolejek, drzew itd.

Ale co do prędkości są tragiczne. Wystarczy zrobić proste doświadczenie. Zbudować dwie, duże kwadratowe macierze w postaci zwykłych tablic i wykonać na nich mnożenie. Następnie zrobić to samo na macierzach zbudowanych na listach. Zauważysz różnicę na korzyść zwykłych tablic.

Natomiast spróbuj utworzyć dużą, wielowymiarową tablicę. Zapewne wystąpią problemy, tu wskaźniki sprawdzą się znacznie lepiej.

Tak, więc wszystko zależy od zastanej sytuacji.

Hmmmm… Tragiczne, to jest budowanie macierzy w ten sposób i nie dowodzi prędkości działania wskaźników, tylko złego podejścia do tematu.

Odpowiadając na postawione w temacie pytanie:

Decyzja należy do Ciebie. Wskaźników używa się głównie do dynamicznej alokacji pamięci. Mówiąc obrazowo, chcesz mieć tablię, ale jej rozmiar znany będzie dopiero w momencie działania programu. Inny przykład to klasa bazowa, której pewne klasy potomne korzystają z różnych komponentów składowych (jedna używa wszystkich, inne tylko trzech, inne żadnych) ale implementacja pewnych metod, które operują na tych kompomentach wskazana jest na poziomie klasy bazowej (np. aktualizacja wszystkich składowych po zmianie stanu obiektu). Klasa bazowa przechowuje wskaźniki na obiekty, które budują funkcjonalność. Klasy potomne tworzą dynamicznie te obiekty, z których będą korzystać. W momencie np. aktualizacji komponentów, aktualizujemy tylko te, które nie są NULL. Takie podejście pozwala także zwolnić pamięć w jednym miejscu kodu.

Rozgadałem się. Mówiąc krótko, to czy korzystać z wskaźników czy nie zależy od tego co chcesz uzyskać. To samo zrobić można na referencjach w C++. Różnica jest taka, że referencje nie mogą być puste (NULL) (to na minus). Na plus (i to duży) jest to, że nie trzeba zwalniać pamięci.

Brak możliwości zwalniania pamięci z czegoś co nie jest wskaźnikiem, jest dużym minusem. Nie ma już takiej władzy. Jedyną wadą jaką można przypisać wskaźnikom jest troszkę trudniejsze użytkowanie. Ale jeśli się opanuje, to jest bardzo proste. Szybkość jest niewiele mniejsza(dwie instrukcje mov więcej niż w przypadku “zwykłych” zmiennych), a wielu rzeczy by się za pomocą referencji nie zrobiło.

Władzy nad czym? Nad zwalnianiem pamięci? :slight_smile:

Tak, nad tym. W większym projekcie to jest bardzo potrzebne i “zwykłe” obiekty są mało kiedy potrzebne. Przeważnie wskaźniki, alokowane w jednej funkcji, zwalniane w drugiej :wink:

Czasem bez wskaźników oczywiście się nie da, to fakt i kontrola jest niezbędna. Wyżej miałem na mysli prostsze sytuacje np. listę referencji do obiektów, po której nie trzeba sprzątać. Co do dużych projektów, to pracuję przy takim, który ma ok. 700 000 linii kodu. I też częściej korzystamy ze wskaźników niż referencji, np. w sytuacji z klasami bazowymi i ich komponentami, o której pisałem wyżej. :slight_smile:

Kończąc temat dodam od siebie tylko dwie rzeczy.

W większości kompilatorów c++, jak ktoś lubi takie zabawy, można pożonglować wskaźnikami, wymusić parę rzutowań pokombinować i pokazać, że referencje są w pamięci przechowywane jako zwykłe wskaźniki, a że kod pisany przy użyciu referencji jest o wiele bardziej czytelny od tego samego napisanego z użyciem wskaźników to wybór wydaje się oczywisty :slight_smile:

Zaś jedynym miejscem gdzie używanie wskaźników(referencji) może przyśpieszyć program to przekazywanie dużych struktur jako argumenty do funkcji - lepiej przekazać wskaźnik niż kopiować ileś tam bajtów na stos. Ale to się bardziej C tyczy, bo tablice i (chyba) obiekty i tak zawsze są zawsze przekazywane jako wskaźniki/referencje.

p.s. Wskaźniki w C zachęcają do zabaw w stylu “8[a] = 0” i programista czerpie z tego dziwną przyjemność, oczywiście aż do momentu kiedy musi taki czyjś kod zdebugować :wink:

Z tego co się orientuje, to referencja jest tak samo przekazywana jak wskaźnik. Tylko inaczej się do niej odnosi.

Co do wyglądu, to ja wolę wskaźniki niż referencje, “&” bardziej przypomina operator bitowej koniunkcji(dobrze napisałem?) niż referencje :stuck_out_tongue_winking_eye:

Obiekty w C tak samo jak w C++ są kopiowane. Tylko tablice są przekazywane jako wskaźnik(ale to też w C).

Ad. PS.: Tym można fajnie postraszyć nowych. Innego(mądrego) zastosowania tu nie widzę :stuck_out_tongue_winking_eye:

Zgodnie z regułą KISS jeśłi tablica ma mieć stały rozmiar, to nie warto się bawić w udziwnianie kodu. Kod ma być maksymalnie prosty.

Natomiast jeśli prowadzisz bazę danych klientów dużą bazę danych (i ilość pozycji ciągle przybywa) to z pewnością używanie wskaźników jest wskazane, bo można dynamicznie przydzielać tyle pamięci, ile jest potrzebne.