• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Czym różni się referencja od wskaźnika?

VPS Starter Arubacloud
+1 głos
1,506 wizyt
pytanie zadane 9 maja 2021 w C i C++ przez Daaa22 Dyskutant (8,250 p.)

czytałem dużo o tym i nie znalazłem informacji. Przykładowo, czym różnią się te programy?
1.

void zwieksz( int *wartosc )
{
    (*wartosc)++;
}

int main()
{
    int zmienna;
    zwieksz( &zmienna );
    return 0;
}

2.

void zwieksz( int &wartosc )
{
    wartosc++;
}

int main()
{
    int zmienna;
    zwieksz( zmienna );
    return 0;
}

Który jest bardziej wydajny? Który mniej pamięci zajmuje? Kiedy lepiej w argumentach funkcji użyć wskaźnika a kiedy referencji?

komentarz 10 maja 2021 przez Daaa22 Dyskutant (8,250 p.)
z tego co tam przeczytałem to różnica jest tylko w zapisie kodu

1 odpowiedź

+7 głosów
odpowiedź 10 maja 2021 przez NewEraOfPeace Gaduła (4,790 p.)
edycja 4 lipca 2021 przez NewEraOfPeace
 
Najlepsza

Na poziomie języka:

  • Nie ma stałych referencji, są stałe wskaźniki
  • Referencji nie można "zmienić", to znaczy nie możesz mieć referencji do zmiennej A, a następnie zmienić sobie, żeby ta referencja wskazywała na B
  • Referencje nie mogą być nullem (to znaczy, zawsze muszą być poprawne, choć są hacki)
  • Nie można mieć tablicy referencji (m.in. do tego istnieje std::reference_wrapper)

Mamy generalnie 4 rodzaje referencji:

1. int& 
2. const int& 
3. int&& 
4. T&&
  • 1. Referencja do l-wartości (l-value reference), może się "przyczepić" tylko do istniejącego obiektu, dla uproszczenia
    2. Referencja do stałej l-wartości (const l-value reference, const reference w skrócie) (tak, wiem, że często określa się to const referenca (dosłownie - stała referencja), jednak referencje nie mogą być cv-qualified, czyli nie można im przyczepić const/volatile.), może się "przyczepić" do istniejących obiektów, ale również do obiektów tymczasowych, czyli np. literałów (5, 1.5f)
    3. Referencja do r-wartości (r-value reference), przyczepi się tylko do obiektu tymczasowego*, jest używana przy semantyce przenoszenia. (to, że przyczepi się do obiektu tymczasowego nie jest do końca prawdą, bo w całości nie chodzi o to, jaki, czy gdzie obiekt ma lifetime, a bardziej jakiego typu jest jego wartość, lifetime ma znaczenie, ale w zupełnie innej kwestii)
    4. Uniwersalna/przekazująca referencja (universal/forwarding reference) [universal reference to termin nieoficjalny, forwarding reference to termin oficjalny, używane są raczej zamiennie - dzięki @tkz za poprawkę], to już zdecydowanie bardziej skomplikowana rzecz, generalnie T to jakikolwiek template type, który jest dedukowalny. Używane są w generycznym kodzie do szybkiego i prostego przekazywania argumentów. 
    (w powyższym zestawieniu int można zamienić jakimkolwiek typem/niededukowalnym template type, za to T można zastąpić jakimkolwiek dedukowalnym template type)

 

Wskaźniki:

  • Mogą być stałe (T* const)
  • Mogą wskazywać na stałe typy (const T*)
  • Mogą być stałe i wskazywać na stałe typy (const T* const)
  • Można je "zmienić", czyli jeśli mamy wskaźnik do X, to możemy sobie zmienić ten wskaźnik, żeby wskazywał na Y
  • Mogą być nullem (to znaczy - nie mieć wartości, oznacza się to specjalnym literałem nullptr)
  • Korzystanie ze wskaźników może dodawać koszt null checków (sprawdzania, czy wskaźnik jest poprawny, czy nie)
  • Wskaźniki używa się do np. lazy evaluation
  • Wskaźniki używa się jeśli potrzebna jest semantyka opcjonalności (wyobraź sobie, że chcesz zwrócić z/ zaakceptować do funkcji jakiś typ, ale chcesz również dać użytkownikowi możliwość niepodawania go. W funkcji musisz sprawdzić, czy to co podał jest poprawną wartością, czy użytkownik zdecydował nie podawać nic, masz generalnie 3 możliwości - wybrać pewną wartość z wachlarza możliwych wartości typu i ustalić - to znaczy, że nie podałeś nic, użyć wskaźnika, użyć od C++17 std::optional)

Czego lepiej używać.. Hhmm.. to jest generalnie dość cięzkie pytanie, bo (szczególnie od c++11) referencji nie używa się tylko jako bezpieczniejszych zamienników dla pointerów. Generalnie raczej używaj referencji. Na poziomie binarki to może, ale nie musi być praktycznie to samo, kompilator może (tego nie jestem na 100% pewien) lepiej optymalizować referencję, poza tym referencje nie mają kosztu null checkingu, więc w kodzie może faktycznie się okazać, że referencje będą szybsze. Jednak zmiana wskaźnik -> referencja oznacza również inne zastosowanie, przeznacznie, inną semantykę..


Jeśli chodzi o przekazywanie do funkcji, to jeśli:

  • Nie musisz zmienić wartości przekazanej
    • Typ jest trywialny (int, float, char, etc.) -> przekaż by value (czyli po prostu void foo(int x))
    • Typ nie jest trywialny (std::vector, std::string, etc.) -> przekaż przez stałą referencję [ponownie, to skrót myślowy] (czyli po prostu void foo(const std::vector<...>& x))
  • Musisz zmienić wartość przekazaną
    • Przekaż przez referencję
  • ... blah blah semantyka przenoszenia, kod generyczny, srutututu

Dodatkowo mamy opcje widoków (std::string_view, std::span), niby prosty temat, ale nie będę się w nie wgłębiać. 

W twoim przypadku lepiej użyć referencji. W pierwszej funkcji dla poprawności powinieneś sprawdzić, czy przesłany wskaźnik jest nullptr, czy nie.

 

To co napisałem to w zasadzie nie jest wszystko, bo dalej mamy kwestie UB, forwardingu, semantyki przenoszenia, extendowania czasu życia zmiennych, etc., etc., etc.

/ prosiłbym o poprawy, bo pisałem to na szybko, mogło się wkraść sporo błędów /

1
komentarz 10 maja 2021 przez tkz Nałogowiec (42,000 p.)
  • Referencji nie można "zmienić", to znaczy nie możesz mieć referencji do zmiennej A, a następnie zmienić sobie, żeby ta referencja wskazywała na B

std::ref

Mamy generalnie 4 rodzaje referencji:

Tak trochę pokręciłem głową. Właściwie to semantyka wartości. W dodatku, tak zgodnie z dokumentacją, mamy 7 tych wartości w postaci drzewa. Value category https://isocpp.org/files/papers/N4860.pdf#expr.prop Raczej w gwoli uściślenia. 

  Uniwersalna/przekazująca referencja (universal/forwarding reference) [nie jest to oficjalny termin, ale został szeroko zaadoptowany],

Niezupełnie, uniwersalna referencja została użyta przez Meyersa tylko dlatego, że komitet standaryzacyjny pominął to w standardzie 11. Poprawili się później, forwarding reference  jest popraną nazwą. 
W głównej mierze, że referencja && nie odróżnia r-value od l-value. 

void foo(int&& i);
template <typename T>
void bar(T&& i);

foo(1); //okey, przejdzie
int k = 0;
foo(k); //blad kompilacji


bar(1); //okey
int k = 0;
bar(k); //okey

Czego lepiej używać.. Hhmm.. to jest generalnie dość cięzkie pytanie, bo (szczególnie od c++11) referencji nie używa się tylko jako bezpieczniejszych zamienników dla pointerów. Generalnie raczej używaj referencji.

To nie jest trudne pytanie. Referencji używasz zawsze, wskaźników kiedy musisz. 

Na poziomie binarki to może, ale nie musi być praktycznie to samo

Referencja to wskaźnik, wskaźnik to nie referencja. Gdzieś był o tym artykuł. Jak sobie przypomnę, to podepnę. 

kompilator może (tego nie jestem na 100% pewien) lepiej optymalizować referencję

Zależy jaki kod napiszesz. 

oza tym referencje nie mają kosztu null checkingu, więc w kodzie może faktycznie się okazać, że referencje będą szybsze.

To jest tak marginalny koszy dla kompilatora, że można go pominąć właściwie. Na pewno jest utrudnieniem dla programisty. Na szczęście nullptr trochę ratuje. 

 

Ogólnie plusik, spoko wprowadzenie moim zdaniem. 

komentarz 10 maja 2021 przez NewEraOfPeace Gaduła (4,790 p.)
  • Referencji nie można "zmienić", to znaczy nie możesz mieć referencji do zmiennej A, a następnie zmienić sobie, żeby ta referencja wskazywała na B

std::ref

std::ref zwraca std::reference_wrapper, miałem na myśli referencje jako koncept językowy, nie magię biblioteki standardowej

 

Mamy generalnie 4 rodzaje referencji:

Tak trochę pokręciłem głową. Właściwie to semantyka wartości. W dodatku, tak zgodnie z dokumentacją, mamy 7 tych wartości w postaci drzewa. Value category https://isocpp.org/files/papers/N4860.pdf#expr.prop Raczej w gwoli uściślenia. 

 Semantyka wartości to trochę inny, ale tak, powiązany temat. Generalnie referencji nie interesuje nic innego niż tylko lvalue i rvalue

  Uniwersalna/przekazująca referencja (universal/forwarding reference) [nie jest to oficjalny termin, ale został szeroko zaadoptowany],

Niezupełnie, uniwersalna referencja została użyta przez Meyersa tylko dlatego, że komitet standaryzacyjny pominął to w standardzie 11. Poprawili się później, forwarding reference  jest popraną nazwą. 
W głównej mierze, że referencja && nie odróżnia r-value od l-value. 

Szczerze to nawet nie wiem jak się do tego odnieść, bo nie do końca rozumiem o co Ci chodzi. Z oficjalnym terminem, to faktycznie, sprawdziłem i jest to w standardzie, zaraz wrzucę poprawkę, dzięki.

Czego lepiej używać.. Hhmm.. to jest generalnie dość cięzkie pytanie, bo (szczególnie od c++11) referencji nie używa się tylko jako bezpieczniejszych zamienników dla pointerów. Generalnie raczej używaj referencji.

To nie jest trudne pytanie. Referencji używasz zawsze, wskaźników kiedy musisz. 

Jest :p. Bo nie kończy się tylko na tym czy brać referencję czy wskaźnik.


 

komentarz 10 maja 2021 przez tkz Nałogowiec (42,000 p.)

Czepnąłem się poszczególnych punktów. Jako całość raczej spoko. Chociaż też nie uważam, że mam taką wiedzę, żeby wyłapać wszystko. 

std::ref zwraca std::reference_wrapper, miałem na myśli referencje jako koncept językowy, nie magię biblioteki standardowej

W ostatnim punkcje napomknąłeś o bibliotece standardowej, stąd też moja uwaga. 

Szczerze to nawet nie wiem jak się do tego odnieść, bo nie do końca rozumiem o co Ci chodzi. Z oficjalnym terminem, to faktycznie, sprawdziłem i jest to w standardzie, zaraz wrzucę poprawkę, dzięki.

Ogólna uwaga, co do terminologii. Kod raczej dla autora pytania. Osobiście wolę wszystko poparte kodem.   

Jest :p. Bo nie kończy się tylko na tym czy brać referencję czy wskaźnik.

Nie rozumiem za bardzo. 

komentarz 10 maja 2021 przez Daaa22 Dyskutant (8,250 p.)
To kiedy używać referencji a kiedy wskaźnika?
komentarz 10 maja 2021 przez tkz Nałogowiec (42,000 p.)

Podobne pytania

0 głosów
1 odpowiedź 296 wizyt
pytanie zadane 4 marca 2023 w JavaScript przez TOWaD Mądrala (5,700 p.)
0 głosów
4 odpowiedzi 1,126 wizyt
pytanie zadane 21 lutego 2017 w C i C++ przez Danthee Początkujący (260 p.)
+1 głos
2 odpowiedzi 460 wizyt
pytanie zadane 13 września 2015 w C i C++ przez Iras Obywatel (1,800 p.)

92,452 zapytań

141,262 odpowiedzi

319,085 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...