czym się różni jedna gwiazdka od dwóch gwiazdek we wskaźniku

0

Głupie pytanie ale czasem przeglądając czyjś kod widzę takie coś: int ** nazwa_zmiennej i moje pytanie, czym to różni się od zwykłego wskaźnika, tj z jedną gwiazdką?

4

Tym na co wskazuje.
int* wskazuje na miejsce w pamięci gdzie leży jakiś int. int ** wskazuje na miejsce w pamięci gdzie leży wskaźnik który pokazuje na inta.

0

o takie buty chodzi xD dzięki za wyjaśnienie.

3

Najprościej to tak: nie ma czegoś takiego jak podwójna gwiazdka. To są dwie osobne gwiazdki, i każda z nich oznacza to samo: wskaźnik.

A więc - wskaźnik na wskaźnik na inta.

0

W takim dużym uproszczeniu, to wskaźnik do wskaźnika to możemy porównać do tablicy dwuwymiarowej :) Czyli pierwszy wskaźnik pokazuje na wiersz a drugi na kolumnę a w zasadzie na konkretna wartość. Dodając do tego arytmetykę wskaźników i wyłuskiwanie, możemy działać tak samo jak na tablicach bo w zasadzie tablice to wskaźniki :)

0

Napisałem w "dużym uproszczeniu". A czym w takim razie jest tablica jednowymiarowa jak nie wskaźnikiem na jej pierwszy element ? Jeśli chce skopiować tablicę i przekazuję ją do funkcji to przekazuje przecież wskaźnik na jej pierwszy element.
Jeśli mam tablicę text i chce pobrać 5ty element tej tablicy to nic nie stoi na przeszkodzie aby pobrać go wykorzystując do tego arytmetykę wskaźników i wyłuskanie: *(text + 5)

1

@macsurf:

template<int n>
void fun(int (&tab)[n]) { }

To funkcja przyjmująca jednowymiarową tablicę intów jako argument. Skoro tablica to wskaźnik to spróbuj przekazać do tej funkcji wskaźnik.

0

Żeby nie mieszać wskaźnik jest wskaźnikiem a tablica, tablicą :)
Chodzi o to, że coś takiego jak tablica jest nierozerwalnie związana ze wskaźnikami.
To czy będziemy pobierać dane z tablicy w notacji tablicowej czy bezpośrednio używać wskaźników to efekt będzie taki sam, poza nieco większą szybkością na korzyść wskaźników.
Dla tablicy wskaźnik to "narzędzie" :)

A co do tego "uproszczenia" to chodziło mi o to do czego najczęściej się wykorzystuje takiego typu wskaźniki do wskaznikow np. taki int** nazwa_zmiennej, może zostać dynamicznie zaalokowany jakiś obszar pamięci np. 5*5 ( malloc cz new ) i wtedy ten obszar pamięci możemy traktować właśnie jak tablicę dwuwymiarową. Czyli np. możemy sobie dynamicznie zaalokować tablicę na macierz o wielkości ustalonej w trakcie wykonywania programu.

Oczywiście tutaj "A czym w takim razie jest tablica jednowymiarowa jak nie wskaźnikiem na jej pierwszy element ?" jest błąd bo chodziło mi o
"przekazywanie tablicy" np. do funkcji wtedy przekazuje się oczywiście wskaźnik do pierwszego elementu.

1

@macsurf co post to gorzej. Serio.

  1. Chodzi o to, że coś takiego jak tablica jest nierozerwalnie związana ze wskaźnikami. a to niby czemu? Standard definiuje chyba tylko to że można niejawnie konwertować tablicę do wskaźnika na jej pierwszy element i że będzie to wskaźnik na ciągły blok pamięci. To trochę jak z std::string, masz tam metodę c_str() która zwraca wskaźnik na ciagły obszar pamięci w którym będzie zawartość naszego stringa. Ale czy to znaczy nagle że "string to to samo co wskaźnik" albo że "string to to samo co tablica" albo "string jest nierozerwalnie związany ze wskaźnikami"? No chyba nie koniecznie...
  2. To czy będziemy pobierać dane z tablicy w notacji tablicowej czy bezpośrednio używać wskaźników to efekt będzie taki sam pobierać elementy? Ok. Ale jak zrobisz sobie sizeof(tablica) a sizeof(wskaźnik) to jednak wynik będzie trochę inny...
  3. poza nieco większą szybkością na korzyść wskaźników. generalnie mit i bzdura. Tak było może 20 lat temu, dzisiaj jest wręcz przeciwnie, bo zabawa w skakanie wskaźnikami utrudnia korzystanie z cache procesora.
  4. A co do tego "uproszczenia" to chodziło mi o to do czego najczęściej się wykorzystuje takiego typu wskaźniki do wskaznikow, o RLY? Najczęściej? Masz jakieś statystyki? Bo jest znacznie więcej zastosowań dla podwójnych wskaźników, szczególnie w C...
  5. bo chodziło mi o "przekazywanie tablicy" np. do funkcji wtedy przekazuje się oczywiście wskaźnik do pierwszego elementu nie bardzo rozumiem tą "oczywistość". Możesz przekazać albo jedno albo drugie, zależnie od tego jak ci wygodniej. To trochę jak powiedzieć że masz szklanę z wodą i jak cię kolega poprosi o coś do picia to "oczywiście przelewasz wodę do wiadra i dajesz koledze wiadro"... ;] Jasne, możesz tak zrobić. Czasem nawet warto bo takie wiadro ma różne inne ciekawe zastosowania...
0
  1. Przecież to co zwraca c_str() to zwykła tablica znaków czyli w gruncie rzeczy zwraca ci wskaźnik na pierwszy element tej tablicy ( i jak tu mówić, że wskaźniki nie są związane z tablicami :) )
  2. Ale to oczywiste i to nie jest kwestia notacji tablicowej bo ją możesz używać zarówno do tablic, które są alokowane na stosie jak i na stercie. Inaczej się tylko alokuje pamięć na takie tablice. A to, że sizeof() pokazuje ilość w bajtach bloku pamięci ( tablicy ) to tylko kwestia tego, że została ta pamięć zaalokowana na stosie i kompilator zna jej wielkość: char tablica[15] i masz 15 bajtowy blok zaalokowany na stosie a jak zaalokujesz pamięć na stercie np. przez malloc czy new to jak kompilator ma wyliczyć ile zajmuje ten blok ? skoro ta pamięć jest przydzielana w trakcie działania programu ? W obu przypadkach masz tablice czy tam blok przydzielonej pamięci jak zwał tak zwał. Obie notacje wskaźnikowa i tablicowa do odczytu/zapisu jest dozwolona na takich typach danych.
  3. Oczywiście kwestia kompilatora
  4. Przecież nie napisałem, że jest to jeden jedyny sposób na wykorzystywanie wskaźników do wskaźników, zresztą możecie autorowi tematu podać inne :)
  5. Przecież efekt jest taki sam, dasz moja_tablica czy &moja_tablica[0] poza zapisem nie widzę, żadnej różnicy kompilator i tak w obu wypadkach pobiera adres na pierwszy element tablicy ( choć oczywiście nie musi być pierwszy )
2
  1. Tak, ale to nie znaczy że "string" jest tym samym co "tablica znaków"...
  2. Nie "zwał jak zwał". W jednym przypadku masz tablicę a w drugim masz zaalokowany dynamicznie blok pamięci.
  3. Nie, nie kwestia kompilatora, a przynajmniej nie całkiem, prędzej kwestia CPU. Bo ty myślisz o optymalizacji kompilatora polegającej na zamianie odwołań do kolejnych indeksów tablicy poprzez przesuwanie wskaźnika, a ja mówie o read-ahead i data cache procesora. Takie próby "sprytnej" optymalizacji przez używanie wskaźnika mogą się skończyć spadkiem wydajności o rzędy wielkości jak procesor nie zauważy twojej intencji ;]
  4. To sprawdź znów jak zachowa się sizeof jeśli argumentem będzie tablica a jak jeśli będzie nim wskaźnik, skoro uważasz że "nie ma różnicy" ;)
3

Po co się spierać, skoro można po prostu sprawdzić za pomocą samego języka?

#include <iostream>
#include <type_traits>
 
int main() {
  typedef int ArrayType[5];
 
  const bool is_array_pointer =
    std::is_pointer<ArrayType>::value;
  const bool will_array_decay_to_pointer =
   std::is_pointer<std::decay<ArrayType>::type>::value;
 
  std::cout <<  std::boolalpha;
  std::cout << "Is array a pointer? " << is_array_pointer << '\n';
  std::cout << "Will array decay to pointer? " << will_array_decay_to_pointer;
}

Is array a pointer? false
Will array decay to pointer? true

http://ideone.com/Bkgsb5

1

Powtórzę się żeby nie umknęło:

Podwójny wskaźnik używa się przede wszystkim do dynamicznych tablic dwu-wymiarowych w C.
To jeśli chodzi o naukę języka (edukacja w szkole) a nie statystyki występowania.
Jest to ładniejsza forma (bo bez mnożenia) implementacji dynamicznych tablic w C, ale nie jedyna.

Wersja bez-gwiazdkowa: https://www.eskimo.com/~scs/cclass/int/sx9b.html
Wersja jedno-gwiazdkowa (pierwsza w linku): http://www.geeksforgeeks.org/dynamically-allocate-2d-array-c/

Dwu-wymiarowe tablice to najprostszy sposób na pokazanie sensowności podwójnych wskaźników.
Ale oczywiście takie wskaźniki mogą służyć też do innych rzeczy.

1

@vpiotr W języku C podwójny wskaźnik używany jest przede wszystkim do przekazania wskaźnika, który ma być zmieniany w jakiś sposób.

0
kaczus napisał(a):

@vpiotr W języku C podwójny wskaźnik używany jest przede wszystkim do przekazania wskaźnika, który ma być zmieniany w jakiś sposób.

No właśnie dynamiczna dwu-wymiarowa tablica (i każda tablica tablic) do tego poglądu nie pasują.

0
  1. Nigdzie tak nie napisałem :) Co do string to masz tam też przeładowany operator [], tak więc pośrednio odwołuje się do tablicy z charami mimo, iż masz do czynienia z obiektem a nie prostą tablicą w stylu C.
  2. I jedno i drugie jest tablicą ( odpowiednio tablica statyczna i tablica dynamiczna ) czy nazwiesz to tablicą czy blokiem pamięci to nie ma znaczenia bo wiadomo o co chodzi. Ogólnie przyjęło się, że mówimy "tablica X-elementowa" o X-wymiarach.
  3. O tym nigdy nie słyszałem, czyli twierdzisz, że taka "sprytna" optymalizacja ma odwrotny efekt od zamierzonego w stosunku do odwoływania się do indeksów tablicy, przez notację tablicową ?
  4. sizeof() nie jest funkcją tylko operatorem :) a ja napisałem to w kontekście przekazywania tablic do funkcji czyli przekazanie wskaźnika na pierwszy element tablicy :) Tak więc nie ma różnicy czy dasz tab czy &tab[0]
0

@macsurf wskaźnik z tablicą w języku C ma tyle wspólnego, że

  1. nazwa tablicy jest wskaźnikiem na pierwszy jej element
  2. wskaźnik może wskazywać na tablicę - być jej iteratorem, bądź pamiętać jej początek (by wiedzieć co zwolnić, gdy nie będzie potrzebna).
0

Tak więc nie ma różnicy czy dasz tab czy &tab[0]

Jeśli ktoś pomija błędy kompilacji to nie ma. http://ideone.com/pp0zir

0

Na uczelni uczą, w materiałach wykładowych też to jest, że w C/C++ tablicę i wskaźniki można traktować zamiennie i używać takiej formy jaka jest wygodniejsza. Przy demonstracyjnych programikach rzeczywiście to się sprawdza. Jednak jak uczyć to rzetelniej. Czasami można się dowiedzieć, że string jest tylko tablicą znaków zakończoną nullem, a objective-C to obiektowa wersja C.

1

@novpet
Zamiennie traktować nie możesz, juz chyba rozumiem o co @Endrju chodziło, jak mnie strofował, no to - może ma o tyle racji, że powinno sie to inaczej nazwać. Przykład

int tablica[10];
...
tablica++;

zadziała czy nie? Jakby było zamiennie zadziałałoby, ale nie jest.

0

Myślę że ważne w kontekście wyciągania wniosków kiedy co stosować (czy i jak tablicę oraz czy i jak wskaźnik lub wskaźnik-wskaźnik)
(jak już będzie konsensus czym jest ** :->) Warto zerknąć na cały standard bo CERT prowadzi statystyki naruszeń, konsekwencji błędów i ich przyczyn na poziomie kodu:
https://www.securecoding.cert.org/confluence/display/c/CERT+C+Coding+Standard reguły z rozdziału ARR.
Fajne bo są odniesienia do standardów (ISO, CWE, ...). Np. ta ważna informacja że arytmetyka na wskaźnikach ma być zapewniona w ramach wskaźnika na tą samą tablicę oraz 1 element poza (dla C++). Po "wyskoczeniu" poza tablicę, wynik undefined. A większość kompilatorów to "olewa"... :-/

1

Nawet w kontekście tablicy dwuwymiarowej uważam takie określenie za niepoprawne. Tablica dwuwymiarowa ma, zgodnie z nazwą, dwa wymiary. T** to może być co najwyżej tablica wskaźników na tablice (bardziej łopatologicznie, mniej poprawnie: tablica tablic, w C#/Javie: jagged array), z których każda może mieć inny (lub powtarzający się) adres i wielkość (więc wiele wymiarów). Ponadto, powyższe implikuje brak ciągłości danych w takiej pseudotablicy, co jest kolejną różnicą względem prawdziwych tablic (memset(t, 0, sizeof(t)/sizeof(*t)))

1 użytkowników online, w tym zalogowanych: 0, gości: 1