Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

R. 45 - zadanie domowe - pytanie do ppkt b

Ostatnio zmodyfikowano 2019-09-17 20:14
Autor Wiadomość
rottingham
Temat założony przez niniejszego użytkownika
» 2019-08-27 17:24:46
Jaka logika stała za tym, że dodajKoniec() alokuje jeden węzeł i zwalnia inny?

No cóż, patrząc na gafę jaką popełniłem chciałbym wyjaśnić co mi przyświecało w procesie twórczym.

Rozpisałem taki kod:
C/C++
#include <iostream>

using namespace std;

struct Wezel
{
    int liczba;
    Wezel * ogon;
};

struct Lista
{
    Wezel * poczatek;
    Wezel * koniec;
};

int main()
{
    Lista * lista = new Lista;
   
    Wezel * element = new Wezel;
    element->liczba = 1;
    element->ogon = nullptr;
   
    lista->poczatek = element;
   
    cout << element << endl;
   
    delete element;
    element = nullptr;
   
    cout << element << endl;
   
    cout << lista->poczatek << " " << lista->poczatek->liczba << endl;
   
}

Patrząc na wyjście tego kodu wyraźnie - ku mojemu zdziwieniu - mogłem wyzerować Wezel * element co nie powodowało zerowania lista->poczatek. Uznałem, że znalazłem dziwną prawidłowość polegającą na tym, że lista->poczatek nabiera poniekąd pewną autonomię co pozwala mi wykorzystać Wezel * element do tymczasowego przechowania adresu i wyzerowania go, ażeby ponownie go wykorzystać.
P-175133
pekfos
» 2019-08-27 18:03:09
Uznałem, że znalazłem dziwną prawidłowość polegającą na tym, że lista->poczatek nabiera poniekąd pewną autonomię co pozwala mi wykorzystać Wezel * element do tymczasowego przechowania adresu i wyzerowania go
Trochę późno na robienie takich odkryć. Tak działają zmienne.

C/C++
lista->poczatek = element;
// Oba wskaźniki wskazują na ten sam obiekt

cout << element << endl;

delete element;
// Oba wskaźniki zawierają BŁĘDNY ADRES
element = nullptr;
// Wyzerowałeś `element`, a `lista->poczatek` wciąż zawiera BŁĘDNY ADRES
cout << element << endl;

cout << lista->poczatek << " " << lista->poczatek->liczba << endl; // Odwołujesz się do pamięci pod błędnym adresem.
P-175134
rottingham
Temat założony przez niniejszego użytkownika
» 2019-09-01 19:15:26
Okej, napisałem kod, który wiem, że działa. Pytanie: co mogę w nim ulepszyć? Kod wydaje mnie się trochę karkołomny. Czy mógłbym prosić o wskazówki jak go trochę skrócić?

C/C++
#include <iostream>

using namespace std;

struct Wezel
{
    int liczba;
    Wezel * nastepny;
};

struct Lista
{
    Wezel * glowa, * wezel, * ostatni;
};

void utworz( Lista *& lista )
{
    lista = new Lista;
    lista->glowa = lista->wezel = lista->ostatni = nullptr;
}

void dodajKoniec( Lista *& lista, int liczba )
{
    Wezel * nowy = new Wezel;
    nowy->liczba = liczba;
    nowy->nastepny = nullptr;
    lista->wezel = nowy;
   
    if( !lista->glowa )
    {
        lista->glowa = lista->wezel;
        lista->ostatni = lista->wezel;
    }
    else
    {
        lista->ostatni->nastepny = lista->wezel;
        lista->ostatni = lista->wezel;
    }
   
}

void wypisz( Lista * lista )
{
    while( lista->glowa )
    {
        cout << lista->glowa->liczba << " ";
        lista->glowa = lista->glowa->nastepny;
    }
}

void zniszcz( Lista * lista )
{
   
    while( lista->glowa )
    {
        Wezel * tmp = lista->glowa;
        lista->glowa = lista->glowa->nastepny;
        delete tmp;
    }
   
    while( lista->wezel )
    {
        Wezel * tmp = lista->wezel;
        lista->wezel = lista->wezel->nastepny;
        delete tmp;
    }
   
    while( lista->ostatni )
    {
        Wezel * tmp = lista->ostatni;
        lista->ostatni = lista->ostatni->nastepny;
        delete tmp;
    }
   
    lista->ostatni = lista->glowa = lista->wezel = nullptr;
}

int main()
{
    int liczba;
    Lista * lista;
   
    utworz( lista );
   
    while( cin >> liczba && liczba )
         dodajKoniec( lista, liczba );
   
    wypisz( lista );
    zniszcz( lista );
}
P-175155
pekfos
» 2019-09-01 23:43:59
Czy mógłbym prosić o wskazówki jak go trochę skrócić?
Usuń z niego błędy. Masz w kodzie fragmenty których jedynym zadaniem jest wprowadzenie błędów. Błędów które już tu co najmniej raz opisywałem i tłumaczyłem, więc przeczytaj temat od początku.
P-175156
rottingham
Temat założony przez niniejszego użytkownika
» 2019-09-02 01:11:48
W tym kodzie jest struct Wezel zamiast Listy. Mamy użyty typedef Wezel * Lista. Czy jest potrzebny tutaj drugi struct?

C/C++
struct Wezel {...}; // Zamiast 'Lista'
typedef Wezel * Lista; // pewnie trzeba by gdzieś omówić typedef

void utworz( Lista & lista )
{
    lista = nullptr;
}

//..

int main()
{
    int liczba;
    Lista lista;
    utworz( lista )
    std::cout << "Podaj liczby, 0 lub blad konczy:\n";
   
    while( std::cin >> liczba && liczba )
         dodajKoniec( lista, liczba );
   
    std::cout << "Koniec, oto liczby:\n";
    wypisz( lista );
    zniszcz( lista );
}
P-175157
pekfos
» 2019-09-02 23:37:51
Pytasz, bo wracasz do punktu wyjścia? Poprzedni kod jest blisko rozwiązania, wystarczy naprawić błędy.
Z jakiego innego powodu byś implementował niszczenie listy 2 razy - raz zaczynając od początku i raz od końca?
Ty niszczysz listę od początku, od końca i od lista->wezel, cokolwiek to oznacza. Czy dodanie tych wskaźników sprawiło, że alokujesz dynamicznie więcej pamięci? Nie? To zostaw je w spokoju. Po prostu je zignoruj. To znaczy, możesz je wyzerować.. ale nie wywołuj na nich delete!
P-175161
rottingham
Temat założony przez niniejszego użytkownika
» 2019-09-09 00:26:51
Ok. Myślę, że zrozumiałem większość błędów, które popełniałem.
Napisałem kod, który chyba jest najlepszym do tej pory. Jednak mam co do niego pytania: jak napisać funkcję wypisz(), tak aby nie modyfikować wskaźnika lista->glowa? On się modyfikuje mimo, że nie ma operatora referencji. W sumie dziwi mnie to, bo dlaczego, żeby wyzerować Lista * lista potrzebuję użyć w funkcji utworz() referencji, a tutaj następuje modyfikacja bez referencji?
Gdyby udało mnie się ją wypisać bez modyfikowania wskaźnika, to mógłbym w struct Lista mieć tylko dwie składowe.

C/C++
#include <iostream>

using namespace std;

struct Wezel
{
    int liczba;
    Wezel * nastepny;
};

struct Lista
{
    Wezel * glowa, * ogon, * poczatek;
};

void utworz( Lista *& lista )
{
    lista = nullptr;
}

void dodajogon( Lista *& lista, int liczba )
{
    Wezel * nowy = new Wezel;
    nowy->liczba = liczba;
    nowy->nastepny = nullptr;
   
    if( !lista )
    {
        lista = new Lista;
        lista->glowa = lista->ogon = lista->poczatek = nowy;
    }
    else
    {
        lista->ogon = lista->ogon->nastepny = nowy;
    }
}

void wypisz( Lista * lista )
{
    if( lista )
    while( lista->glowa )
    {
        cout << lista->glowa->liczba << " ";
        lista->glowa = lista->glowa->nastepny;
    }
}

void zniszcz( Lista *& lista )
{
    if( lista )
    {
        while( lista->poczatek )
        {
            Wezel * tmp = lista->poczatek;
            lista->poczatek = lista->poczatek->nastepny;
            delete tmp;
        }
        lista->ogon = nullptr;
    }
}

int main()
{
    int liczba;
    Lista * lista;
    utworz( lista );
   
    while( cin >> liczba && liczba )
         dodajogon( lista, liczba );
   
    wypisz( lista );
    zniszcz( lista );
}
P-175173
pekfos
» 2019-09-09 01:04:22
Gdyby udało mnie się ją wypisać bez modyfikowania wskaźnika, to mógłbym w struct Lista mieć tylko dwie składowe.
Skopiuj do zmiennej lokalnej?

On się modyfikuje mimo, że nie ma operatora referencji. W sumie dziwi mnie to, bo dlaczego, żeby wyzerować Lista * lista potrzebuję użyć w funkcji utworz() referencji, a tutaj następuje modyfikacja bez referencji?
Wskaźnik i referencja to prawie to samo. Przekazując Lista * lista przekazujesz wskaźnik przez wartość, ale to co jest pod wskazanym adresem możesz modyfikować, tak jakbyś to przekazał przez referencję. W języku C nawet nie było referencji, więc to
C/C++
void f( int & x )
{
    x = 123;
}
się robiło tak
C/C++
void f( int * x )
{
    * x = 123;
}
P-175174
1 2 3 « 4 » 5 6
Poprzednia strona Strona 4 z 6 Następna strona