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

Dynamiczna alokacja pamięci dwuwymiarowej na wskaźnikach

Mały hosting, OGROMNE możliwości
0 głosów
483 wizyt
pytanie zadane 20 lutego 2018 w C i C++ przez Kurczak Użytkownik (940 p.)

Cześć, chciałem zrobić prosty program, który ma za zadanię wypisać na ekran tabliczkę mnożenia. Ćwiczę użycie wskaźników i chciałbym prosić o wskazanie błędu oraz ewentualne wyjaśnienie błędu myślowego.

#include <iostream>

using namespace std;

int main()
{
    /*DYNAMICZNA ALOKACJA PAMIECI*/
    int rozmiar;
  cout<<"Podaj rozmiar"<<endl;
  cin>>rozmiar;
  int **tab=new int *[rozmiar];
  for(int i=0;i<rozmiar;i++)
  {
      *(tab+i)=new int [rozmiar];
  }
  /*------------------------*/

  /*TABLICZKA MNOZENIA*/
  for(int i=0;i<rozmiar;i++){
    for(int j=0;j<rozmiar;j++)
  {
      **(tab)=(i+1)*(j+1);
      cout<<**tab<<" ";
      *(tab++);
  }
  cout<<endl;
  }
  /*----------------------*/

  /*CZYSZCZENIE PAMIECI*/
  for(int i=0;i<rozmiar;i++)
  {
      delete [] *tab;
      tab++;
  }
  delete []tab;
}

 

2 odpowiedzi

+2 głosów
odpowiedź 20 lutego 2018 przez Eryk Andrzejewski Mędrzec (164,260 p.)

Masz błędy w sekcjach TABLICZKA MNOZENIA i CZYSZCZENIE PAMIECI.

Ja poprawiłem kod pierwszej z tych sekcji na:

for (int i = 0; i < rozmiar; i++)
{
    for(int j = 0; j < rozmiar; j++)
    {
        tab[i][j] = (i + 1) * (j + 1);
        std::cout << tab[i][j] << " ";
    }
    std::cout << '\n';
}

Odwołuję się do poszczególnych elementów tablicy (co prawda tablicy dynamicznej, ale można je traktować bardzo podobnie) przy pomocy []. Moim zdaniem błąd popełniasz tutaj:

*(tab++);

Zastanów się czemu. wink

No i druga sprawa, przy czyszczeniu pamięci. Najpierw inkrementujesz wskaźnik tab, a następnie próbujesz zdealokować dane które zostały wcześniej zaalokowane dynamicznie. Ale po inkrementacji wskaźnika, wskazuje on już na zupełnie inny adres, więc prawdopodobnie każesz systemowi operacyjnemu zdealokować dane, które nie zostały zaalokowane, lub też takie, które nie należą do Ciebie.

Ja poprawiłem ten kod na taki:

for (int i = 0; i < rozmiar; i++)
{
      delete [] tab[i];
}
delete [] tab;

Znowu, używam operatora [] do odwoływania się do poszczególnych elementów tablicy.

Tak przy okazji, to odradzam używanie gołych operatorów new i delete. Nie jest to zbyt bezpieczne narzędzie. Jeżeli potrzebujesz dynamicznej tablicy, to prawdopodobnie możesz też użyć jednego z istniejących w STL kontenterów.

Inną sprawą jest, że zamiast martwić się ręcznym zwalnianiem obszarów zaalokowanej przez siebie pamięci (o czym można łatwo zapomnieć), lepiej jest korzystać z takiego mechanizmu jak inteligentne wskaźniki. Ale jeśli się jeszcze uczysz, to przyjdzie na to czas. smiley

+2 głosów
odpowiedź 20 lutego 2018 przez mokrowski Mędrzec (158,960 p.)
Eryk już Ci odpowiedział. Ja tylko dodam że im szybciej uciekniesz od takich technik na korzyść kontenerów biblioteki standardowej, tym szybciej nauczysz się samego języka. Oczywiście przećwicz wskaźniki żeby docenić o ile łatwiej programuje się na kontenerach... Trochę taka "fala" albo "frycowe" :-)

Po operacjach "podwójnych wskaźników" masz w pamięci "rzeszoto alokacji pamięci porozrzucanej tu i ówdzie". To powoduje że taka struktura jest i paskudna w obsłudze (bo wskaźniki) i mało wydajna w większości zastosowań.

Jeszcze inną sprawą jest wydzielanie do funkcji tego co robisz. U Ciebie to będą wszystkie /* KOMENTARZ */. Nie powinny być w main() a w oddzielnych funkcjach. Zmienna rozmiar powinna być typu size_t bo to on jest przeznaczony dla wszelkiego rodzaju rozmiarów. W konsekwencji także indeksy pętli będą size_t

Ostatnio był podobny temat. Tu masz link https://forum.pasja-informatyki.pl/328072/tablica-nieprostokatna#a328118
komentarz 20 lutego 2018 przez Kurczak Użytkownik (940 p.)
Ok, czyli dwuwymiarowej tablicy zaalokowanej dynamicznie nie mogę wyobrazić sobie jako danych ułożonych kolejno w pamięci, tylko są to raczej porozrzucane fragmenty, więc operacje na wskaźnikach nie mają sensu? Gdybym jednak zaalokował tablicę dwuwymiarową statycznie, dał na nią wskaźnik, to wtedy wszystkie dane w pamięci są ustawione w jednym "bloku"?
komentarz 20 lutego 2018 przez mokrowski Mędrzec (158,960 p.)
edycja 20 lutego 2018 przez mokrowski

Operacje na takiej tablicy jak najbardziej mają sens! Pamiętać tylko należy że poszczególne wiersze mogą leżeć ... daleko od siebie w pamięci. Powiem Ci że można zrobić także i taki numer z widokiem na pamięć:

#include <iostream>
#include <iomanip>

// Uwaga: Zamierzone wartości stałe w kodzie...

int main() {
    // Płaska tablica 25 elementowa
    int * table = new int[25];

    // Wypełniona wartościami...
    for(auto i = 0U; i < 25; ++i) {
        table[i] = i + 1;
    }

    //
    // Widok na tablicę 5 x 5 ze wskazaniem na poprzednio alokowaną pamięć.
    // 
    int (*myView)[][5] = reinterpret_cast<int(*)[][5]>(table);

    for(auto row = 0U; row < 5; ++row) {
        for(auto col = 0U; col < 5; ++col) {
            std::cout << std::setw(5) << (*myView)[row][col];
        }
        std::cout << '\n';
    }

    delete [] table;
}

Tylko ani to wygodne ani fajne (ze względu na bardzo niebezpieczne rzutowanie).

Podobne pytania

0 głosów
1 odpowiedź 2,019 wizyt
pytanie zadane 4 lipca 2017 w C i C++ przez kyly Początkujący (260 p.)
0 głosów
1 odpowiedź 568 wizyt
+1 głos
0 odpowiedzi 1,263 wizyt

93,715 zapytań

142,629 odpowiedzi

323,261 komentarzy

63,258 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

Twierdza Linux. Bezpieczeństwo dla dociekliwych

Aby uzyskać rabat -10%, użyjcie kodu pasja-linux, wpisując go w specjalne pole w koszyku.

...