Tablica dwuwymiarowa na stercie

0

W C++, a nawet w C jest możliwa tablica dwuwymiarowa na stosie, ale jej wielkość musi być stała i określona w chwili kompilacji.

int StackArray[10][10];

Czy taki twór tak naprawdę tworzy tablicę 100-elementową, a wszystkie odniesienia są odpowiednio przeliczane?

Natomiast jak szukałem w internecie, jak zrobić tablicą dwuwymiarową na stercie, to co znalazłem opis takiej tablicy, to tak naprawdę znalazłem opis tworzenia jednowymiarowej tablicy wskaźników, która odnosi się do tablic jednowymiarowych. Teoretycznie, da się zrobić klasę obsługującą tablicę dwuwymiarową. Piszę na szybko, bez testowania, więc mogą być błędy:

#include <vector>
class ArrayInt2dim
{
public:
  ArrayInt2dim(int W_, int H_)
  {
    W = W_;
    H = H_;
    Data.reserve(W * H);
  }

  void Set(int X, int Y, int Val)
  {
    Data[Y * H_ + X] = Val;
  }

  int Get(int X, int Y)
  {
    return Data[Y * H_ + X];
  }
private:
  std::vector<int> Data;
  int W;
  int H;
}

To co wyżej napisałem, to jest możliwie najprościej, oczywiście warto napisać szablon dla dowolnego typu, przeciążyć operator [], zaimplementować zmianę rozmiaru z zachowaniem zawartości i wiele więcej.

Pytanie brzmi: Jaki typ ze standardowej biblioteki C++20 tworzy tablice dwuwymiarowe (ogólnie, wielowymiarowe), która funkcjonowałaby tak samo, jak dwuwymiarowa na stosie lub jednowymiarowa z przeliczaniem współrzednych?

Chodzi o coś takiego, tyle, że zamiast ArrayInt2dim<int> byłaby nazwa typu ze standardowej biblioteki;

int main()
{
  // Tablica 10x10 na stosie
  int StackArray[10][10];
  
  // Deklaracja tablicy na stercie
  ArrayInt2dim<int> * HeapArray = NULL;
  
  // Tworzenie tablicy 10x10 na stercie w trakcie pracy programu
  HeapArray = new ArrayInt2dim<int>(10, 10);
  
  // Użytkowanie obu tablic w identyczny sposób
  int SomeVal1, SomeVal2, SomeVal3, SomeVal4;
  for (int Y = 0; Y < 10; Y++)
  {
    for (int X = 0; X < 10; X++)
    {
      // Odczyt elementów do zmiennych
      SomeVal1 = StackArray[X][Y];
      SomeVal2 = HeapArray[X][Y];
  
      // Zapis elementów z zmiennych lub funkcji
      StackArray[X][Y] = SomeVal3;
      HeapArray[X][Y] = SomeVal4;
    }
  }
  
  // Niszczenie tablicy na stercie w trakcie pracy programu
  delete HeapArray;
}

To też tak napisane najprościej, jak można, żeby było widoczne, o co pytam. Normalnie, to zamiast zwykłego wskaźnika lepiej użyć unique_ptr lub shared_ptr.

2

W C++ tablice w stylu C muszą mieć stały rozmiar. Tak mówi standard.
W C od wersji C99 jest coś takiego jak Variable-length Array (VLA) i tablice o zmiennej długości są dozwolone i wymagana przez standard C.
Przy czym począwszy od C23 VLA są już tylko opcjonalne i kompilator nie musi ich wspierać.

Efekt jest taki, że wiele kompilatorów C++ daje możliwość VLA dla C++ jako rozszerzenie języka.
MSVC nigdy nie wdrożyło VLA, więc tam nigdy nie zadziała, ani dla C ani dla C++.

W standardzie C++ nie ma kontenera dwu lub więcej wymiarowej tablicy.
Jednak począwszy od C++23 pojawia się std::mdspan, który potrafi przerobić ciągły zakres wartości, na widok, który zachowuje się jak tablica wielowymiarowa. Przy czym wielkość poszczególnych wymiarów, możę być określona statycznie (podczas kompilacji) lub dynamicznie (run time).

0

możesz próbować takich rzeczy ale tylko w c++ którego notabene używasz

std::vector<std::vector<int>> *vect = new std::vector<std::vector<int>>; // prawie wszystko będzie na stercie(oprócz samego pointera)

std::vector<std::vector<int>> vect; // same elementy na stercie

std::vector<std::vector<*int>> vect; // a tu już chyba tłumaczyć nie trzeba


0

W C++ tablice w stylu C muszą mieć stały rozmiar. Tak mówi standard.

To tak, natomiast tablicę na stercie można tworzyć o dowolnym rozmiarze i ją niszczyć w dowolnym momencie. Faktem jest, że nie jest możliwa zmiana rozmiaru istniejącej tablicy. Pytanie nie jest o tworzenie tablic z możliwością zmiany wielkości w trakcie cyklu życia, tylko jest o tworzenie i usuwanie tablic wielowymiarowych na stercie.

W standardzie C++ nie ma kontenera dwu lub więcej wymiarowej tablicy.
To jest chyba odpowiedź na pytanie. Rozumiem, że wielowymiarowe tablice możliwe są tylko na stosie, gdzie rozmiar jest sztywny i znany przed kompilacją. Na stercie, to bezpośrednio można utworzyć tylko tablicę jednowymiarową. Tworzenie tablicy tablic czy tablicy wskaźników, to jest trochę workaround, jednak efekt jest taki, jakiego się oczekuje.

std::vector<std::vector<int>> vect;

To jest najbliższe temu, o co ja pytam, czyli tablica dwuwymiarowa z możliwością ustalenia wymiarów w trakcie pracy programu i z dostępem poprzez vect[X][Y]. Z drugiej strony, nie jest możliwe zniszczenie i utworzenie nowej tablicy, tylko można dodawać i usuwać elementy w ramach cyklu życia. Ale to i tak w celu zbudowania tablicy jest potrzebne dodanie w pętli tylu elementów, ile wynosi wymiar X i dla każdego elementu zarezerwowanie pamięci tyle, ile wynosi Y (właściwie to wystarczy reserve). Oczywiście, że można zrobić funkcję CreateArray(std::vector<std::vector<int>> &V, int Width, int Height), która to zrobi.

0

Z drugiej strony, nie jest możliwe zniszczenie i utworzenie nowej tablicy, tylko można dodawać i usuwać elementy w ramach cyklu życia

vect = {} \\ niech mnie ktoś poprawi jak się mylę z pamięci piszę

stare elementy idą do krainy wiecznych łowów. O ile mnie pamięć nie myli(trzeba sprawdzić z standardem) w nowszych wersjach standardu od 17 chyba od razu też rozmiar będzie 0 ale to trzeba by doprecyzować.
edit:
Tak swoją drogą ja zaczynam myśleć czy ty też nie potrzebowałbyś co kolwiek jakiś alokatorów
taki luźny przykład
https://howardhinnant.github.io/stack_alloc.html

1

Widzę, że pojawiła się edycja pytania z dodatkowym kodem:

andrzejlisek napisał(a):

Chodzi o coś takiego, tyle, że zamiast ArrayInt2dim<int> byłaby nazwa typu ze standardowej biblioteki;

int main()
{
  // Tablica 10x10 na stosie
  int StackArray[10][10];
  
  // Deklaracja tablicy na stercie
  ArrayInt2dim<int> * HeapArray = NULL;
  
  // Tworzenie tablicy 10x10 na stercie w trakcie pracy programu
  HeapArray = new ArrayInt2dim<int>(10, 10);
  
  // Użytkowanie obu tablic w identyczny sposób
  int SomeVal1, SomeVal2, SomeVal3, SomeVal4;
  for (int Y = 0; Y < 10; Y++)
  {
    for (int X = 0; X < 10; X++)
    {
      // Odczyt elementów do zmiennych
      SomeVal1 = StackArray[X][Y];
      SomeVal2 = HeapArray[X][Y];
  
      // Zapis elementów z zmiennych lub funkcji
      StackArray[X][Y] = SomeVal3;
      HeapArray[X][Y] = SomeVal4;
    }
  }
  
  // Niszczenie tablicy na stercie w trakcie pracy programu
  delete HeapArray;
}

To też tak napisane najprościej, jak można, żeby było widoczne, o co pytam. Normalnie, to zamiast zwykłego wskaźnika lepiej użyć unique_ptr lub shared_ptr.

Ten szablon klasy ArrayInt2dim używa std::vector, który domyślnie trzyma dane właśicie (elementy tablicy) na stercie.
W tym wypadku robinie HeapArray = new ArrayInt2dim<int>(10, 10); jest bezcelowe i prowadzi do potencjalnych błędów.
Począwszy od C++11/14 jawne używanie operatora delete/new jest niewskazane. Powinno się użważ std::make_shared (c++11) i std::make_unique (c++14) - co prawie całkowicie rozwiązuje problem potencjalnych wycieków pamięci.

W C++ nie ma możliwości utworzenia na stosie tablicy o dynamicznym rozmiarze (bez względu na liczbę wymiarów), o czym pisałem wyżej.
Jeśli potrzebujesz tablicę wielowymiarową o statycznym rozmiarze, która jest na stosie użyj:

std::array<std::array<int, 5>, 10> tab2d{};

Jak ma być na stercie, to ten szablon ArrayInt2dim jest jak najbardziej poprawny. Można mu dodać różne funkcjonalności, by był bardziej przyjazny.

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