Δείκτες και Δομές Ένας βασικός «δομικός λίθος». Όλη μαζί η δομή (struct) ονομάζεται κόμβος ή Node και αποτελείται από - Το μέλος Info οποιουδήποτε τύπου: int, άλλο struct (InfoField) - To μέλος ink τύπου δείκτη σε Node. Info ink typedef struct NodeTag { InfoField Info; struct NodeTag *ink; } NodeType;
Απεικονίσεις Λιστών και ενδείξεις τέλους τους : a a: b b: g g: null
Χρησιμοποιούμε εικόνες του τύπου A Info ink Info ink Info ink Όπου NU δηλώνει τον δείκτη που δεν δείχνει πουθενά. Ένδειξη τέλους, όχι αυτόματος τρόπος. (*NU).x είναι σφάλμα (όχι πάντα διαγνώσιμο) Info ink NU
Προσοχή σε... ifetime-διάρκεια Ζωής (3 κατηγορίες μνήμης): - Καθολικές στατικές μεταβλητές - Auto-Stack (τοπικές). Αυτόματη διαχείριση (μπλοκ). - Heap. Οποιοδήποτε αντικείμενο δημιουργείται με malloc ισχύει μέχρι να το αποδεσμεύσουμε με free. Η διαχείριση Heap είναι ευθύνη του προγραμματιστή. Ανακύκλωση: Όταν δεν χρειαζόμαστε άλλο τότε free. Alias-Συνώνυμα: Πολλαπλοί τρόποι αναφοράς για το ίδιο αντικείμενο στη μνήμη. Αιωρούμενοι (dangling) δείκτες: τι συμβαίνει αν ένας δείκτης δείχνει σε θέση μνήμης που δεν ισχύει;
Συνδεδεμένη λίστα //Ο τύπος κόμβος typedef struct NodeTag { InfoField Info; struct NodeTag *ink; } NodeType; Info ink // Ο τύπος δείκτη σε κόμβο typedef NodeType *NodePtr; // Η δήλωση δείκτη αρχής της λίστας NodePtr ; // πάντα χρειάζεται μια αρχή
Εισαγωγή σε συνδεδεμένη λίστα (4 βήματα). META από πού (αρχή, μέση, τέλος???)???????????? Info;ink Info;ink Info;ink Τα αρχικά βήματα 1, 2 είναι κοινά σε κάθε περίπτωση 1. Δημιουργούμε νέο κόμβο Ν NodePtr N; N = malloc(sizeof(nodetype)) 2. Βάζουμε τιμές στο Info του Ν Ν->Info= N Info;ink
Βήματα 3+4 για την σύνδεση του Ν (περιπτώσεις A, B) Α. Εισαγωγή στην αρχή (ισχύει και για κενή λίστα) 3. Τιμή στο ink του Ν N->ink = ;//Ισχύει και αν η ==Κενή(==NU) 4. Τον κάνουμε τον πρώτο κόμβο της λίστας. =N; Info;ink Info;ink Info;ink 4 N 3 Info;ink
Β. Εισαγωγή σε άλλη θέση (META από προδείκτη P) Info;ink Info;ink Info;ink P 3. Τιμή στο ink του Ν N->ink = P->ink; 4. Τον ενσωματώνουμε στη λίστα P->ink = N; P Info;ink Info;ink Info;ink P N 4 3
Αν έχουμε δείκτη ast και κάνουμε εισαγωγή στο τέλος πριν Info;ink Info;ink Info;ink μετά Info;ink Info;ink Info;ink ast ast N Info;ink
... η Εισαγωγή στο τέλος (P==ast) Βήματα 1, 2, 3, 4 όπως πριν + 5ο 1. Δημιουργούμε νέο κόμβο NodePtr N; Ν = malloc(sizeof(nodetype)) 2. Βάζουμε τιμές στο Info του Ν Ν->Info=. 3. Τιμή στο ink του Ν N->ink = P->ink; //ή N->ink=NU; 4. Τον ενσωματώνουμε στη λίστα ως τελευταίο P->ink = N; //ή ast->ink = N; 5. Έλεγχος και Ενημέρωση Δείκτη ast if (P==ast) ast = N; Είναι σωστό; Τι γίνεται αν ο τελευταίος είναι και ο πρώτος;
Διαγραφή Κόμβου (με/χωρίς ast)????????? Info;ink Info;ink Info;ink...... H first last????????? Info;ink Info;ink Info;ink......
Διαγραφή του πρώτου κόμβου typedef struct NodeTag { InfoField Info; struct NodeTag *ink; } NodeType;... T if (!=NU) // υπάρχουν στοιχεία? { NodeType *Τ = ; // προσωρινός δείκτης για free = ->ink; // Νέα τιμή του o επόμενος if ( == NU) // διαγραφή μοναδικού κόμβου ast = NU; // τότε αλλάζει και ο ast free(τ); // ελευθέρωσε τον κόμβο } else ;// ένδειξη εντοπισμού λάθους
Διαγραφή άλλου κόμβου (ΜΕΤΑ από τον προδείκτη P) Info;ink Info;ink Info;ink... P T if ((P!=NU)&&(P->ink!=NU))// υπάρχει επόμενο; { NodeType *T=P->ink // προσωρινός δείκτης (free) P->ink = T->ink;// Νέα τιμή του ο επόμενος if (T == ast) // διαγραφή τελευταίου κόμβου; ast = P; free(t); // ελευθέρωσε τον κόμβο } else ;// ένδειξη εντοπισμού λάθους
Διαγραφή τελευταίου κόμβου ΠΑΝΤΑ με προδείκτη, όχι με χρήση του ast, παρόλο που δείχνει ήδη στον τελευταίο κόμβο. first last Info;ink Info;ink Info;ink...... Το free(ast) αφήνει έναν αιωρούμενο δείκτη και λάθος τιμή για το ast. Μπορούμε να βρούμε τον προηγούμενο δείκτη P μόνο αν περάσουμε από P=first, μέχρι P->ink==ast. first last Info;ink Info;ink......?
Προτείνεται 1) Πριν τον προγραμματισμό συνδεδεμένων δομών, να σχεδιάζετε εικόνες των συνδεδεμένων δομών σας για κάθε περίπτωση. 2) Απεικονίστε στους κόμβους τις μεταβλητές και τις τιμές τους. Σχεδιάστε σε διαδοχικά βήματα τις αλλαγές που θέλετε να κάνετε (εισαγωγή, διαγραφή, κλπ) 3) Διατηρείτε πάντα κάποια σύνδεση. Ένα συνηθισμένο λάθος είναι να ελευθερώνεται η μνήμη πριν φτιαχτούν οι συνδέσεις. Η σειρά ενημέρωσης των συνδέσεων είναι σημαντική. 4) Ελευθερώνετε την μνήμη που δεν χρειάζεστε.