Wskaźniki i struktury Programowanie C, LA Anna Gogolińska
Wskaźniki i struktury Jednym z ól struktury może być wskaźnik na zmienną tyu tej struktury. Deklaracja jest z użyciem formy: ty *. Poza tym olem, struktura może (i owinna) zawierać inne ola już niekoniecznie wskaźniki. struct lista{ ; int klucz; double klucz2; struct lista * wsk; W takiej sytuacji zazwyczaj tworzy się nową zmienną orzez wskaźnik i rzydzielenie mu amięci. struct lista * ; = (struct lista*) malloc(sizeof(struct lista )); Dla orównania wskaźnik na int (ełna analogia): int * w = (int *)malloc(sizeof(int));
Tworzenie Można sobie wyobrazić, że oniższe instrukcje tworzą: struct lista * ; = (struct lista*)malloc(sizeof(struct lista)); klucz1 klucz2 wsk Zaraz o stworzeniu (rzydzieleniu amięci) ola w strukturze są uste, należy nadać im wartości. Robi się to jak w rzyadku zwykłych wskaźników (dodanie * owoduje że odwołujemy się do obiektu wskazywanego rzez wskaźnik) oraz jak w rzyadku struktur z użyciem.
Nadanie wartości (*).klucz1 = 2; (*).klucz2 = 3.2; 2 3.2 (*).wsk = NULL; NULL Zais ten jest jednak niewygodny (należy używać nawiasów onieważ. ma wyższy riorytet niż *). Dlatego zastąiono zais (*). rzez -> (myślnik i znak większości). Oba zaisy są równoważne. ->klucz1 = 5; ->klucz2 = 7.1; ->wsk = NULL; 5 7.1 NULL
Listy Jeśli mamy już jedną zmienną (jak n. z orzednich slajdów) i stworzymy drugą możemy ustawić, aby nowo utworzona wskazywała na (a nie na NULL jak jest to w rzyadku na orzednim slajdzie). W ten sosób będziemy mieć dwie ołączone zmienne. struct lista * nowy = (struct lista*) malloc(sizeof(struct lista )); nowy->klucz1 = 2; nowy->klucz2 = 5.4; nowy->wsk = ; nowy 2 5.4 5 7.1 NULL
Listy dodawanie elementów A gdyby owtórzyć czynność z orzedniego slajdu i stworzyć trzecią zmienną, która będzie wskazywać na orzednio stworzoną? Aby kod mógł być uniwersalny (a nie ciągle zmieniać nazwy iewszej zmiennej w szeregu) należy ustalić jedną zmienną, która zawsze będzie wskazywać na oczątek ciągu zmiennych w naszym rzyadku będzie to. Po dodaniu kolejnego elementu z rzodu należy rzestawić na ten element. struct lista * nowy = (struct lista*) malloc(sizeof(struct lista )); nowy->klucz1 = 2; nowy->klucz2 = 5.4; nowy->wsk = ; = nowy; nowy 2 5.4 5 7.1 NULL
Listy Porzez owtarzanie czynności z orzedniego slajdu owstanie łańcuch zmiennych, którego długość będzie się mogła zmieniać i będzie mogła być teoretycznie nieskończona (ogranicza nas wielkość amięci). Taki łańcuch nazywany jest listą i można o niej myśleć jak o tablicy struktur, która nie ma ustalonej długości. while(warunek){ nowy struct lista * nowy = (struct lista*) malloc(sizeof(struct lista )); nowy->klucz1 = a; nowy->klucz2 = b; nowy->wsk = ; = nowy; xx yy xx yy xx yy 2 5.4 5 7.1 NULL
Usuwanie z listy (oczątek) Z listy usuwamy elementy z użyciem funkcji free() (zwalnianie amięci, rzeciwnie do malloc). Jeśli chcemy usunąć ierwszy element należy amiętać, żeby wierw rzestawić, aby wskazywało na drugi, a o usunięciu ierwszy element list. /*zakładamy że to zmienna globalna więc nie trzeba rzekazywać jej do funkcji*/ void usun(void){ struct lista *om; om = ; = ->wsk; free(om); om Tworzymy omocniczą zmienną którą ustawiamy na oczątek, aby zaamiętać go, kiedy już rzesuniemy.
Usuwanie z listy (oczątek) Z listy usuwamy elementy z użyciem funkcji free() (zwalnianie amięci, rzeciwnie do malloc). Jeśli chcemy usunąć ierwszy element należy amiętać, aby wierw rzestawić, aby wskazywało na drugi, a o usunięciu ierwszy element list. /*zakładamy że to zmienna globalna więc nie trzeba rzekazywać jej do funkcji*/ void usun(void){ struct lista *om; om = ; = ->wsk; free(om); om Przesuwamy - teraz wskazuje na drugi element. Przed rzesunięciem ->wks to był właśnie ten drugi element.
Usuwanie z listy (oczątek) Z listy usuwamy elementy z użyciem funkcji free() (zwalnianie amięci, rzeciwnie do malloc). Jeśli chcemy usunąć ierwszy element należy amiętać, aby wierw rzestawić, aby wskazywało na drugi, a o usunięciu ierwszy element list. /*zakładamy że to zmienna globalna więc nie trzeba rzekazywać jej do funkcji*/ void usun(void){ struct lista *om; om = ; = ->wsk; Usuwamy element om na rysunku ozostawiony jest on na szaro, w rzeczywistości o wykonaniu instrukcji już nie istnieje. Mamy listę o 1 krótszą, a jej ierwszym elementem jest. free(om); om
Usuwanie z listy (środek) Jeśli chcemy usunąc element ze środka należy amiętać, aby wierw rzestawić wskaźnik jego orzednika na element nastęny, aby nie owstała dziura. /*zakładamy że to zmienna globalna, argumentem jest numer elementu do usunięcia, jest to element ze środka, nie koniec i nie oczątek listy */ void usun2(int nr){ int i; struct lista * om, *o; om = ; o = NULL; for(i = 0; i < nr; i++){ o = om; om = om->wsk; o->wsk = om->wsk; free(om); om o Tworzymy dwie omocnicze zmienne, z których om będzie wskazywać usuwany element, a o jego orzednik. Początkowo om wskazuje na oczątek listy, a o na NULL.
Usuwanie z listy (środek) Jeśli chcemy usunąc element ze środka należy amiętać, aby wierw rzestawić wskaźnik jego orzednika na element nastęny, aby nie owstała dziura. /*zakładamy że to zmienna globalna, argumentem jest numer elementu do usunięcia, jest to element ze środka */ void usun2(int nr){ int i; struct lista * om, *o; om = ; o = NULL; for(i = 0; i < nr; i++){ o = om; om = om->wsk; o->wsk = om->wsk; free(om); om o W ętli rzesuwamy się o liście. Pętla trwa aż nie dojdziemy do elementu o numerze odanym do usunięciu (nie srawdzamy czy doszliścy do końca listy). Najierw o wskazuje na tą samą zmienną co om.
Usuwanie z listy (środek) Jeśli chcemy usunąc element ze środka należy amiętać, aby wierw rzestawić wskaźnik jego orzednika na element nastęny, aby nie owstała dziura. /*zakładamy że to zmienna globalna, argumentem jest numer elementu do usunięcia, jest to element ze środka */ void usun2(int nr){ int i; struct lista * om, *o; om = ; o = NULL; for(i = 0; i < nr; i++){ o = om; om = om->wsk; o->wsk = om->wsk; free(om); o om Nastęnie om jest rzesuwany na kolejny element (kolejny to ten na który on wskazuje). Dzięki tym oeracjom om będzie wskazywać element do usunięci, a o jego orzednik.
Usuwanie z listy (środek) Jeśli chcemy usunąc element ze środka należy amiętać, aby wierw rzestawić wskaźnik jego orzednika na element nastęny, aby nie owstała dziura. /*zakładamy że to zmienna globalna, argumentem jest numer elementu do usunięcia, jest to element ze środka */ void usun2(int nr){ int i; struct lista * om, *o; om = ; o = NULL; for(i = 0; i < nr; i++){ o = om; om = om->wsk; o->wsk = om->wsk; free(om); o om Aby nie owstała dziura w liście, ustawiamy, że elementem nastęnym o o będzie element nasteny dla om (czyli dwa elementy dalej) leiej widać to na obrazku.
Usuwanie z listy (środek) Jeśli chcemy usunąc element ze środka należy amiętać, aby wierw rzestawić wskaźnik jego orzednika na element nastęny, aby nie owstała dziura. /*zakładamy że to zmienna globalna, argumentem jest numer elementu do usunięcia, jest to element ze środka */ void usun2(int nr){ int i; struct lista * om, *o; om = ; o = NULL; for(i = 0; i < nr; i++){ o = om; om = om->wsk; o->wsk = om->wsk; free(om); o Teraz już możemy usunąć zmienną om. Lista jest taka jakiej oczekiwalismy.
Przeglądanie listy Nieraz należy rzejrzeć całą listę (n. aby ją wyisać). Można to wykonać dzięki wartości NULL na końcu. /* bedzie oczatkiem listy i zmienna globalna*/ void rzejrzyj(){ struct lista *om; om = ; Pomocnicza zmienna om, która wskazuje na oczątek listy, aby orzez rzesuwanie go nie stracić. while(om!= NULL){ W ętli srawdzamy czy nie doszliśmy do końca koniec rozoznajemy onieważ ma on wartość NULL. oeracja na elemencje n. rintf( %d\n, om->klucz1); om = om->wsk; Przesuwamy om na nastęny element, czyli ten na który on obecnie wskazuje. Ponieważ om na oczątku równy jest oczątkowi listy, a na końcu jej końcowi to rzejrzane zostaną wszystkie elementy.
Uwagi i rady W rzykładach okazano tylko dodawanie elementów na oczątek. Jak wiadomo z zajęć z algorytmów elementy można dodawać i usuwać z oczątku lub z końca w zależności od struktury, którą tworzymy (stos, kolejna). W zależności od otrzeb można również stworzyć listę w której wybieramy sobie gdzie wstawić element lub który usunąć (usuwanie okazane na slajdach). Początki i końce (w zależności od otrzeb też inne elementy) kolejek czy stosów najwygodniej jest zadeklarować jako zmienne globalne. Jeśli zrobi się inaczej mogą wystąić trudności w isaniu funkcji aby nie modyfikowały one tylko lokalnych wartości.
Uwagi i rady Wszystkie wskaźniki w nowo utworzonych zmiennych najleiej jest wyzerować (rzyisań NULL). Należy amiętać o rzyisywaniu wartości NULL wszędzie gdzie jest to otrzebne onieważ tylko dzięki niej jesteśmy w stanie rozoznać czy znajdujemy się na końcu listy. Częstym błędem jest nierzydzielanie amięci dla elementów jeśli tylko tworzymy nowy element (wówczas kiedy rzyisujemy jego olom wartości) należy wierw rzydzielić amięć. Wykonując oeracje na elementach należy zazwyczaj rozróżnić rzyadek kiedy oerujemy na ierwszym, ostatnim i środkowych elementach (jak w rzyadku usuwania).