Wskaźnikowa implementacja struktur listowych – czy program jest dobry?

0

Cześć,
mam małe pytanko. Niestety nie znalazłem wielu źródeł informacji o wskaźnikowych strukturach listowych, moja wiedza opiera się na tym co wyniosłem z wykładu, a także co znalazłem w tym artykule. Oto co w wyniku tak zdobytej wiedzy skleiłem:

#include <iostream>

using namespace std;

main()

{
    struct osoba {
        int wiek, wzrost;
        osoba* nastepna = 0;
    };

    osoba* pierwsza = 0;
    osoba* nowa = new osoba;

    cin >> nowa->wiek;
    cin >> nowa->wzrost;

    if (pierwsza == 0)
        pierwsza = nowa;

    else {
        osoba* temp = pierwsza;

        while (temp->nastepna) {
            temp = temp->nastepna;
        }

        temp->nastepna = nowa;
        nowa->nastepna = 0;
    }
}

program się kompiluje więc de facto uznaję to za sukces. Mam jednak wątpliwości co do tej linijki. Jest ona żywcem skopiowana z powyższego linku

while (temp->nastepna) {
    temp = temp->nastepna;
} 

Czy chodzi tutaj o to, że dopóki temp będzie przesyłać coś do zmiennej nastepna, dopóty będzie to znaczyć, że pamięć jest zajęta? Jeśli tak to dlaczego nie muszę przejść do kolejnej komórki pamięci by sprawdzić, czy jest ona pusta. Mojej teorii przeczy też fakt, że śmieci z pamięci też są "czymś" i mogłby sprawić, że pętla jest bez sensu.

3
Tomek Polak napisał(a):

Niestety nie znalazłem wielu źródeł informacji o wskaźnikowych strukturach listowych […]

Żartujesz? Materiałów na temat list jedno- i dwukierunkowych jest w sieci masa – sam zobacz.

Czy chodzi tutaj o to, że dopóki temp będzie przesyłać coś do zmiennej nastepna, dopóty będzie to znaczyć, że pamięć jest zajęta?

Nie, choć nie rozumiem co to zdanie oznacza. Dlatego bezpieczniej jest się nie zgodzić i wyjaśnić o co chodzi.

Ta pętla ma za zadanie przeiterować po całej liście i przerwać działanie wtedy, gdy temp będzie posiadać wskazanie na ostatni węzeł listy. Wtedy właśnie temp->nastepna zawierać będzie null, co sprawi, że pętla zostanie przerwana.

Czyli w skrócie, służy do odnalezienia ostatniego węzła niepustej listy. W Twoim kodzie ostatni węzeł jest wyszukiwany po to, aby móc dodać nowy węzeł do listy. Choć tak się tego robić nie powinno, ale na tym etapie nie chcę Ci mieszać w głowie optymalizacjami.

Jeśli tak to dlaczego nie muszę przejść do kolejnej komórki pamięci by sprawdzić, czy jest ona pusta.

Bo wtedy wyskoczyłbyś poza listę – w zmiennej temp znajdowałby się null, z którym niczego by nie można było zrobić. Dlatego w warunku pętli sprawdzany jest wskaźnik na kolejny węzeł względem bieżącego, tak aby w zmiennej został wskaźnik na ostatni węzeł.

Mojej teorii przeczy też fakt, że śmieci z pamięci też są "czymś" i mogłby sprawić, że pętla jest bez sensu.

Śmieci z pamięci są losowymi danymi, które mogą (ale nie muszą) spowodować błędu w wykonaniu programu. Dlatego nie igraj z nimi i pisz kod w taki sposób, aby z nich nie korzystał. ;)

0

okej rozumiem, pozostaje ta kwestia. Skąd program "wie", mając taki kod, że ma przejść do kolejnej komórki pamięci?

temp = temp->nastepna;
0
Tomek Polak napisał(a):

okej rozumiem, pozostaje ta kwestia. Skąd program "wie", mając taki kod, że ma przejść do kolejnej komórki pamięci?

temp = temp->nastepna;

temp zawiera adres w pamięci. Pod tym adresem jest struktura, która w polu nastepna tez trzyma adres kolejnej struktury. Czyli aktualny adres po jaki masz iść zastępujesz następnym adresem, który tam znajdziesz. A ponieważ to zastępowanie odbywa się w pętli, program idzie od adresu do adresu.

0
Tomek Polak napisał(a):

Skąd program "wie", mając taki kod, że ma przejść do kolejnej komórki pamięci?

Zależy co konkretnie chcesz wiedzieć.

Jeśli chodzi o znaczenie tego kodu, to temp jest zwykłym wskaźnikiem, wskazującym na strukturę opisującą węzeł listy. temp->nastepna to taki sam wskaźnik. Najpierw wykonywana jest prawa strona, a następnie wykonywane jest przypisanie. Tak więc najpierw pozyskiwany jest wskaźnik na kolejny węzeł, a następnie wpisywany jest do zmiennej.

Natomiast jeśli chodzi o to, dlaczego taki kod działa, to po części wyjaśnia to powyższy paragraf (kolejność wykonywania – prawa strona najpierw, przypisanie później). Jeżeli chcesz wiedzieć jeszcze więcej na temat tego zapisu, to zejdź niżej i zobacz jaki kod assemblera jest generowany dla tej linijki. Dla wygody możesz skorzystać z internetowego narzędzia Compiler Explorer. Ale to raczej dla zaawansowanych.

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