W tym wywiadzie dla Slashdot zacytowano Linusa Torvaldsa:
Widziałem zbyt wiele osób, które usuwają pojedynczo połączony wpis na liście, śledząc wpis „poprzedni”, a następnie usuwając wpis, robiąc coś w stylu
if (prev)
prev-> next = entry-> next;
else
list_head = entry-> next;i ilekroć widzę taki kod, po prostu mówię „Ta osoba nie rozumie wskaźników”. I to niestety dość powszechne.
Ludzie, którzy rozumieją wskaźniki, używają „wskaźnika do wskaźnika pozycji” i inicjują go za pomocą adresu nagłówka list_head. A następnie, gdy przeglądają listę, mogą usunąć wpis bez użycia żadnych warunków, po prostu wykonując polecenie „* pp = entry-> next”.
Jako programista PHP nie dotknąłem wskaźników od czasu wprowadzenia do C na uniwersytecie dziesięć lat temu. Uważam jednak, że jest to rodzaj sytuacji, z którą powinienem przynajmniej się zapoznać. O czym mówi Linus? Szczerze mówiąc, gdybym został poproszony o zaimplementowanie połączonej listy i usunięcie elementu, powyższy „zły” sposób to sposób, w jaki bym to zrobił. Co muszę wiedzieć, aby kodować, jak mówi Linus najlepiej?
Pytam tutaj, a nie o przepełnienie stosu, ponieważ tak naprawdę nie mam z tym problemu w kodzie produkcyjnym.
prev
zamiast całego węzła, możesz po prostu zapisać lokalizacjęprev.next
, ponieważ to jedyna rzecz, którą jesteś zainteresowany. Wskaźnik do wskaźnika. A jeśli to zrobisz, unikniesz głupotyif
, ponieważ teraz nie masz dziwnego przypadkulist_head
bycia wskaźnikiem spoza węzła. Wskaźnik do nagłówka listy jest wówczas semantycznie taki sam jak wskaźnik do następnego węzła.Odpowiedzi:
Używając moich umiejętności L331 MS Paint:
Pierwotnym rozwiązaniem jest wskazywanie węzłów za pośrednictwem
curr
. W takim przypadku sprawdzisz, czy następny węzełcurr
ma wartość usuwania, a jeśli tak, zresetuj wskaźnikcurr
węzłanext
. Problem polega na tym, że nie ma węzła, który wskazywałby na początek listy. Oznacza to, że musi istnieć specjalny przypadek, aby to sprawdzić.Zamiast tego Linus (prawdopodobnie) proponuje nie zapisanie wskaźnika w bieżącym badanym węźle, ale wskaźnik do wskaźnika do bieżącego węzła (oznaczonego
pp
). Operacja jest taka sama - jeślipp
wskaźnik wskazuje na węzeł o właściwej wartości, resetujeszpp
wskaźnik.Różnica pojawia się na samym początku listy. Chociaż nie ma węzła, który wskazywałby na początek listy, w rzeczywistości istnieje wskaźnik do nagłówka listy. I to jest tak samo wskaźnik do węzła, jak
next
wskaźnik innego węzła . Dlatego nie ma potrzeby stosowania specjalnej klauzuli na początku listy.źródło
list_head
wskazanie czegoś za pomocąnext
węzła, który wskazuje na pierwszy rzeczywisty element danych (iprev
zainicjował się na ten sam obojętny obiekt). Nie podoba mi się pomysłprev
wskazywania na coś innego rodzaju, ponieważ takie sztuczki mogą wprowadzić niezdefiniowane zachowanie poprzez aliasing i sprawić, że kod będzie niepotrzebnie wrażliwy na układ struktury.prev
wskazywania na Węzły, wskazuje na wskaźniki. Zawsze wskazuje na coś tego samego typu, mianowicie wskaźnik do węzła. Twoja sugestia jest zasadniczo taka sama -prev
wskaż coś „znext
węzłem”. Jeśli odrzucisz powłokę, otrzymasz tylkolist_head
wskaźnik początkowy . Lub innymi słowy - coś, co jest zdefiniowane tylko przez posiadanie wskaźnika do następnego węzła, jest semantycznie równoważne ze wskaźnikiem do węzła.list_head
inext
będzie zawierało ten sam „rodzaj” wskaźnika. Być może nie problem w C, ale być może problematyczny w C ++.Przykład kodu
Jeśli potrzebujemy trochę logiki, aby zniszczyć węzeł, możemy po prostu dodać wiersz kodu na końcu:
źródło