dowolny wskaźnik

dowolny wskaźnik
YA
  • Rejestracja:prawie 14 lat
  • Ostatnio:prawie 13 lat
  • Postów:36
0

Dzień dobry.

Mój problem dotyczy wskaźników.
Np:

Kopiuj
wchar_t* zm1;
wchar_t** zm2;
wchar_t*** zm3;
//...
wchar_t/*n[*]*/ zm_n;

Przepraszam za ten zapis ale myślę, że wiecie o co mi chodzi (ostatnia linia). Jak stworzyć wskaźnik o dowolnej ilości "zagnieżdżeń" wiedząc jednak ile tych "zagnieżdżeń" ma być (parametr podany w konstruktorze np. jakiejś klasy).
Gdyby makra miały jakiś mechanizm pętli byłoby możliwe sklejenie takiego wyrażenia. Szukałem w sieci jakiegoś rozw. ale bezskutecznie.

ZJ
  • Rejestracja:około 14 lat
  • Ostatnio:około 12 lat
1

Chciałem już napisać, że się nie da, ale na szczęście (niestety dla ludzi, którzy pracują nad utrzymaniem kodu) w C++ można zastosować lepsze makra, czyli szablony.

Kopiuj
 
template <class T,int n>
struct MultiStarPointer 
{
	typedef typename MultiStarPointer<T,n-1>::pointer *  pointer;
};
 
template <class T>
struct MultiStarPointer<T,0> 
{
	typedef T pointer;
};

int main(){
  MultiStarPointer<char,1>::pointer p;
	char * v;
	p=v;
	MultiStarPointer<int,3>::pointer pi3;
	int*** pi3test;
	pi3=pi3test;
}

Ogólnie to po co tobie takie rozwiązanie. Jeżeli potrzebujesz tworzyć dużo różnych wskaźników ("wielo gwiaździstych"), to bym się zastanowił nad strukturą kodu oraz czy nie można tego problemu rozwiązać w inny sposób.

rafal__
"bym się zastanowił nad strukturą kodu oraz czy nie można tego problemu rozwiązać w inny sposób. " - np. drzewo
ZJ
W drzewie każdy wierzchołek przechowuje wskaźnik do wierzchołków dzieci, więc potrzebujesz tylko jedną "gwiazdkę" (ewentualnie dwie dla bardziej ogólnego drzewa, tj z tablicą dzieci).
YA
  • Rejestracja:prawie 14 lat
  • Ostatnio:prawie 13 lat
  • Postów:36
0

Dziękuję za odp. Nie wiedziałem, że tak się nawet da.
Natomiast czy przyjąłem dobre założenia, tego nie wiem. Chciałem zrobić najpospolitszą rzecz czyli tablicę n-wymiarową i zamknąć to w jakiejś klasie.
Ktoś zapyta o sens robienia takiego czegoś, przecież są gotowe rozw. jak np. boost multi arrays. To wszystko prawda, jednak chciałem stworzyć sobie coś takiego sam, dostosować do konkretnych potrzeb i przy okazji czegoś się nauczyć. Jeśli ktoś ma jakieś sugestie co do sposobu alokacji pamięci (lepszy sposób), niech pisze śmiało.

KA
  • Rejestracja:około 21 lat
  • Ostatnio:około 4 lata
  • Postów:1652
1

Można to rozwiązać na wiele sposób. Na ogół rozwiązania uniwersalne są gorsze.

  1. Czy liczba wymiarów w tej tablicy może ulegać zmienia? Tzn czy tablica @da może nagle stać się 3D?
  2. Czy liczba wymiarów tablicy jest ustalana w czasie kompilacji czy w czasie działania programu?
  3. Czy tablice mogą się rozszerzać? Jeżeli nie, to czy całkowite wymiary tablicy są ustalane w czasie kompilacji czy w czasie działania programu?
  4. Czy tablice będą w większości przypadków puste? Tzn np same zera dla tablic z liczbami (generalnie konstruktor domyślny). // dość istotne, zwłaszcza gdy tablice mogą być duże
  5. W nawiązaniu do poprzednio: Złożoność czasowa czy pamięciowa? Co optymalizować?

A najlepiej napisz do czego to ma być stosowane ;)

Wibowit
  • Rejestracja:około 20 lat
  • Ostatnio:około 10 godzin
3

Rozwiązanie zaproponowane przez Zjarka ma sporą wadę - dostanie się do elementu w tablicy n-wymiarowej wymaga zdereferencjowania n wskaźników.

Tablice wielowymiarowe o każdym wymiarze stałym (tzn np bez sytuacji, w której rzędy tablicy mają różną długość) można łatwo emulować za pomocą tablicy jednowymiarowej i schematu Hornera. Np:

Kopiuj
int a[A][B][C];
a[x][y][z] = v;
// ten kod jest równoważny z poprzednim
int b[A * B * C];
b[(x * B + y) * C + z] = v;

"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
msm
Fajne rozwiązanie - można to teraz zapakować w ładną klasę i cieszyć się obiektowością :)
YA
  • Rejestracja:prawie 14 lat
  • Ostatnio:prawie 13 lat
  • Postów:36
0

Dziękuję za pomoc.
Liczba wymiarów tablicy jest stała, ich długość raczej też (nad tym jeszcze pomyślę ale raczej stała) i mogą to być wartości określone zarówno w czasie działania programu jak i kompilacji. Tablice nie będą puste (hm zdawało mi się, że w rzeczywistości tablica nigdy nie jest pusta). Mogą służyć do tymczasowego przechowania jakiś danych np inf. o modelu 3d, po spełnieniu swojej funkcji ulegną zniszczeniu lub do trzymania jakiś informacji dłuższy czas. Złożoność ... nie wiem czy dobrze rozumiem ale ważniejsza będzie tu raczej lepsza wydajność niż użycie pamięci. Wybaczcie mi braki w wiedzy.
Sprawdzę też pomysł Wibowita, ale czy tutaj można, po przeciążeniu operatora [], zastosować potem wyłuskanie jakiejś wartości np. przez zapis: tab3d[x][y][z]?

ZJ
Da się to zrobić, ale w tym przypadku o wiele prostsza jest notacja nawiasowa (x,y,z), na pewno prostsza do wydajnego napisania. Jeżeli zależy ci na wydajności i operacji na takich tablicach to użyj jakieś biblioteki opartej na macierzowych bibliotekach Fortrana, np. Armadillo.
YA
  • Rejestracja:prawie 14 lat
  • Ostatnio:prawie 13 lat
  • Postów:36
0

Emulowanie tablicy fajny pomysł, jest tylko mały problem związany z narzutem w postaci dodatkowych obliczeń podczas pobierania jakiegoś elementu. Nic mądrego nie wymyślę i zastanawiałem się czy po prostu nie zrobić tego w podobny sposób jak np. w przypadku std::vector, czyli do już istniejącego obiektu dodać kolejny itd., czyli wystarczyłaby tylko tablica 1-wymiarowa i druga n-wymiarowa, która używałaby tej pierwszej:

Kopiuj
//zgodnie z tym założeniem np. tablica
int arr[x][y][z];
//tablica o wymiarze x ze wskaźnikami do x tablic 
//x tablic o wymiarach y ze wskaźnikami do już właściwych tablic
//x * y tych właściwych tablic o wymiarach z
//np.
template <typename TYPE> class Simple_array 
{ 
    //... 
};
typedef Simple_array <Simple_array*> _Simple_array_;

template <typename TYPE> class Array
{
    //...
    Array( unsigned dimensions_count, unsigned dimensions[] )
    {
	//przydział pamięci
    }
    //...
};
edytowany 1x, ostatnio: yet_another_bug
ZJ
  • Rejestracja:około 14 lat
  • Ostatnio:około 12 lat
0

Narzut związany z obliczeniami jest dość mały, stawiałbym na to, że mniejszy niż wynikający z dereferencji wskaźników (na pewno tak było jak robiłem trójwymiarową tablicę bitów, ale o narzuconym rozmiarze (128), więc mnożenie było zredukowane do przesunięcia bitowego). Dodatkowo przy tablicy wskaźników masz gorsze cache'owanie, co też może się przełożyć na wydajność.

//Edit
Dodatkowo iterowanie po jednym wymiarze (jakimkolwiek) ogranicza się do dodawania stałej do indeksu, dzięki czemu narzut arytmetyczny praktycznie nie ma znaczenia.

edytowany 1x, ostatnio: Zjarek
Wibowit
  • Rejestracja:około 20 lat
  • Ostatnio:około 10 godzin
0

Schemat Hornera podałem w sumie jako ciekawostkę. Skoro A, B i C to stałe, to ich iloczyny też są stałymi, a więc można je raz przeliczyć na wstępie i nie używać schematu Hornera. Dlatego:

Kopiuj
int b[A * B * C];
// zamiast
b[(x * B + y) * C + z] = v;
// można użyć
b[x * B * C + y * C + z] = v;

Kompilator powinien sobie obliczyć te iloczyny w czasie kompilacji, ewentualnie jeżeli wymiary są obliczane przed tworzeniem instancji klasy, to można to wymnożyć w konstruktorze. Zostaje więc kod:

Kopiuj
b[x * BC + y * C + z] = v;

Ilość mnożeń i dodawań pozostała taka sama jak w schemacie Hornera, ale tutaj można wykonywać mnożenia równolegle. Obecnie (tzn od czasów Pentiumów) procesory są wielopotokowe i mogą robić kilka niezależnych operacji naraz, a więc na przykład w tym przypadku.

Różnice w szybkości iteracji pomiędzy implementacją z emulacją za pomocą mnożenia, a implementacją z wielokrotną dereferencją zależy od wielkości najniższych wymiarów. Jeśli są one niskie to narzut w implementacji z wielokrotną dereferencją jest duży.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit
YA
  • Rejestracja:prawie 14 lat
  • Ostatnio:prawie 13 lat
  • Postów:36
0

Ok, jeszcze raz dziękuję wam za wszystkie uwagi.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.