Transponowanie macierzy

0

Cześć, piszę sobie klasę do obsługi działań na macierzach:

class Macierz
{
public:
    Macierz(int a, int b);
    Macierz(int a, int b, int c);
    ~Macierz();
    void wypisz(void) const;
    void transponuj(void);

private:
    int **tab;
    int x;
    int y;
}; 

I chcę napisać metodę 'transponuj' która zrobi transponowanie macierzy (wiersz to kolumna, a kolumn to wiersz).

void Macierz::transponuj(void)
{
    int **tab_trans = new int *[y];
    for (int k = 0; k < y; ++k)
    {
        tab[k] = new int[x];
    }
    for (int i = 0; i < y; i++)
    {
        for (int j = 0; j < x; j++)
            tab_trans[i][j] = tab[j][i]; //tu wywala!
    }
    tab = tab_trans;
    swap(x, y);
    //tu jeszcze zwolnienie pamięci dla tab_trans
} 

W komentarzu zaznaczyłem miejsce w którym program sie wysypuje, mógłby ktoś spojrzeć i dać wskazówkę co jest nie tak?

1
    int **tab_trans = new int *[x];
    for (int k = 0; k < x; ++k)
    {
        tab[k] = new int[y];
    }

Oraz zapomniałeś zwolnić tab wraz z wierszami.

0

No tak, ale wydaje mi się że problem nie leży w samym utworzeniu tab_trans (tablica ta ma mieć rozmiar odwrotny do tab), tylko w przypisaniu do tab_trans, odpowiedniej wartości tab.

0

Tak jak pisze @_13th_Dragon masz memleak, bo nie zwalniasz tablicy (http://en.wikipedia.org/wiki/Memory_leak).
To nie bezpośrednio powód tego że Ci nie działa, to kolejny błąd. Powinieneś zwalniać tab zanim zmienisz jego wartość.

Jak o zwalnianiu mowa:

//tu jeszcze zwolnienie pamięci dla tab_trans

Jeśli fatycznie zwalniasz pamięć dla tab_trans, to zwalniasz całą tablicę (w tym momencie tab_trans pokazuje na to samo na co tab) - nic dziwnego że nie działa.

Jeszcze możesz się przyjrzeć czy x i y są poprawnie inicjalizowane.

0

Mój pomysł przedstawiał się następująco:

  • mam tablicę którą chcę transponować, powiedzmy tab[x][y]
  • rezerwuję sobie miejsce na nową tablicę, tylko z odwróconymi rozmiarami tab_trans [y][x]
  • przepisuje wartości tab do tab_trans
  • zwalniam pamięć dla starej tab
  • niech tab pokazuje na to samo co tab_trans (tab = tab_trans)
  • zwalniam pamięć dla tab_trans

Nie ma tutaj chyba mowy o wyciekach pamięci?

1
  • zwalniam pamięć dla starej tab

W kodzie który podałeś tego nie ma, dlatego zauważamy. Tak, z tym nie byłoby wycieku pamięci.

  • zwalniam pamięć dla tab_trans

Ale w ten sposób zwalniasz pamięć dla tego co jest aktualnie.

Prościej będzie opisać strzałkami. Przy wskaźnikach zawsze sprawdzają się strzałki ;)

początkowy stan:

tab -----------> początkowa tablica

alokowanie pamięci:

tab -----------> początkowa tablica
tab_trans  ----> nowa tablica

po pętli for robiąca transponowanie:

tab -----------> początkowa tablica
tab_trans  ----> tablica po transponowaniu (wynik)

po zwolnieniu pamięci dla tab:

tab -----------> [zwolniona pamięć, śmieci]
tab_trans  ----> tablica po transponowaniu (wynik)

tab = tab_trans:

tab ---------\
              -----> tablica po transponowaniu (wynik)
tab_trans  --/

I to wszystko czego potrzeba - tab wskazuje już na wynik końcowy, a tab_trans (tzn. sam wskaźnik, cztery/osiem bajtów) zniknie za chwilę bo jest zmienną lokalną.

Ale jeśli teraz zwolnisz tab_trans (czyli pamięć na którą wskazuje), to skończysz z (katastrofa):

tab ---------\
              -----> [zwolniona pamięć, śmieci]
tab_trans  --/
0

Okej, jeżeli chodzi o alokację pamięci wszystko już rozumiem, dzięki.

Jednak problemem jest teraz przepisanie wartości ze starej tablicy do nowej, gdyż np. linijka tab_trans[i][j] = tab[j][i] nie działa, czyli trzeba tu wymyślić chyba jakiś sprytniejszy sposób, niż ten znany ze statycznych tablic.

0

Mógłbyś dać cały kod macierzy? Bo w tym co podałeś nie ma (albo nie widzę na pierwszy rzut oka) błędu, może coś wcześniej idzie źle.

0
 class Macierz
{
public:
    Macierz(int a, int b);
    Macierz(int a, int b, int c);
    ~Macierz();
    void wypisz() const;
    void transponuj();
    //void wyznacznik();

private:
    int **tab;
    int x;
    int y;
};
#include "Macierz.h"
#include <iostream>

using namespace std;

Macierz::Macierz(int a, int b)
{
    x = a;
    y = b;
    tab = new int *[a];
    for (int i = 0; i < a; ++i)
    {
        tab[i] = new int[b];
    }
}

Macierz::Macierz(int a, int b, int c)
{
    x = a;
    y = b;
    tab = new int *[a];
    for (int i = 0; i < a; ++i)
    {
        tab[i] = new int[b];
    }
    //wypełnienie tablicy argumentem c
    for (int i = 0; i < a; i++)
    {
        for (int j = 0; j < b; j++)
            tab[i][j] = c;
    }
}

Macierz::~Macierz()
{
    for (int i = 0; i < x; ++i)
    {
        delete tab[i];
    }
    delete tab;
}

void Macierz::wypisz(void) const
{
    for (int i = 0; i < x; i++)
    {
        for (int j = 0; j < y; j++)
        {
            cout << tab[i][j] << " ";
        }
        cout << endl;
    }
}

void Macierz::transponuj()
{
    int **tab_trans = new int *[y];
    for (int k = 0; k < x; ++k)
    {
        tab[k] = new int[x];
    }
    for (int i = 0; i < y; i++)
    {
        for (int j = 0; j < x; j++)
        {
            tab_trans[i][j] = tab[j][i]; //tu wywala!
        }
    }
    tab = tab_trans;
    swap(x, y);
} 

Jeszcze nie wcieliłem w życie (w kod), w metodzie transponuj zwalniania pamięci, o którym rozmawialiśmy, ale to chyba nie jest powód tego że program się wysypuje?

0
    int **tab_trans = new int *[y];
    for (int k = 0; k < x; ++k)

Hint: warunek pętli.

PS nazywaj sensowniej parametry;
PPS //wypełnienie tablicy argumentem c - c jest parametrem, nie argumentem;
PPPS raczej rozmiar macierzy nie może być ujemny, więc int jest nie na miejscu. unsigned int/size_t/(...) byłoby lepszym wyborem.

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