Δημοσιεύτηκε: 02 Ιούλ 2011, 00:58
migf1 έγραψε:Καλημέρα παιδιά, 1000 ευχαριστώ για τα καλά σας λόγια. Είναι χαρά μου να βοηθάω όπου μπορώ.
Φίλε Strarlight, ο κώδικάς σου έχει προβληματάκια:
1. στον ορισμό του struct customer πρέπει να βάλεις και τη λέξη customer στην 1η γραμμή, ώστε να ξέρει μέσα στο σώμα του σε τι τύπο αναφέρεται ο δείκτης next. Έτσι όπως το έχεις τώρα:
- Κώδικας: Επιλογή όλων
typedef struct { // <--- λάθος, πρέπει: typedef struct customer {
int id;
struct customer *next;
} customer;
ουσιαστικά ο τύπος του *next είναι άγνωστος (άμα δεις ο gcc σου βγάζει warnings όταν πας να εκχωρήσεις στον δείκτη ->next ένα άλλον δείκτη τύπου customer). Επίσης, είναι καλή πρακτική τους πρόσθετους τύπους να τους γράφεις με κεφαλαίο το 1ο τους γράμμα, οπότε το παραπάνω γίνεται:
- Κώδικας: Επιλογή όλων
typedef struct customer {
int id;
struct customer *next;
} Customer;
2. Όταν πας να τυπώσεις την λίστα σου, την διατρέχεις σωστά με έναν βοηθητικό δείκτη: helper, αλλά μέσα στο loop αντί να τυπώνεις μόνο το id του τρέχοντος κόμβου (helper->id), εσύ το βάζεις να τυπώσει τα id και των 3 κόμβων της λίστας, σε ένα printf. Και μάλιστα δεν τυπώνεις καν το id του εκάστοτε helper, αλλά κάνεις απευθείας πρόσβαση στους: head, one και two...
- Κώδικας: Επιλογή όλων
while(helper!=NULL)
{
printf("%d %d %d",head->id , one->id , two->id); // <-- λάθος, πρέπει: printf("%d , helper->id);
helper=helper->next;
}
Οπότε θα πρέπει είτε να διατρέξεις τη λίστα με loop και να τυπώνεις το id του εκάστοτε τρέχοντος κόμβου μέσα στο loop...
- Κώδικας: Επιλογή όλων
// τύπωμα της λίστας με loop
Customer *helper = head;
while ( helper != NULL )
{
printf("%d ", helper->id);
helper = helper->next;
}
... είτε να τυπώσεις απευθείας σε ένα printf τα id των head, one και two χωρίς loop (αν και αυτό δεν έχει νόημα, γιατί π.χ. αν έχουμε 100 στοιχεία στη λίστα δεν υπάρχει περίπτωση να τα τυπώσουμε με αυτό τον τρόπο)...
- Κώδικας: Επιλογή όλων
printf("\n%d %d %d\n", head->id, one->id , two->id); //<--- μη χρήσιμος τρόπος (απλά για δοκιμή)
3. Όταν απελευθερώνεις τη μνήμη για τους κόμβους...
- Κώδικας: Επιλογή όλων
free(head);
free(head->next); // <--- λάθος, το head είναι ήδη κατεστραμμένο
free(one->next);
κάνεις το σφάλμα να απελευθερώνεις πρώτα τον κόμβο στον οποίον δείχνει ο head και κατόπιν να τον χρησιμοποιείς ξανά (ενώ έχει ήδη απελευθερωθεί) για να απελευθερώσεις το head->next. Ο λόγος που ο compiler δεν σου βγάζει error είναι επειδή η free() δεν κάνει έλεγχο εγκυρότητας για το όρισμα που της περνάς (θεωρητικά αν είναι άκυρο ή NULL δεν κάνει τίποτα, αλλά η υλοποίησή της εξαρτάται από τον compiler). Όταν τρέχεις το παραπάνω εκτελέσιμο αρχείο δεν σου βγάζει segmentation-fault επειδή ο gcc έχει έναν ας τον πούμε εσωτερικό μηχανισμό προστασίας για κάποιες περιπτώσεις. Αν αυτό τον κώδικα τον κάνεις compile σε άλλον compiler το εκτελέσιμο αρχείο πιθανότατα θα βαρέσει seg-fault όταν θα πάει να εκτελέσει το: free( head->next ). Όπως και να χει πάντως είναι λανθασμένο!
Η σωστή σειρά για να αποδεσμεύσεις τους κόμβους είναι η ανάποδη από αυτή που έχεις βάλει εσύ...
- Κώδικας: Επιλογή όλων
free(one->next);
free(head->next);
free(head);
ή ακόμα καλύτερα...
- Κώδικας: Επιλογή όλων
free( head->next->next );
free( head->next );
free( head );
Πάντως όπως και με την περίπτωση του τυπώματος, στην πράξη απελευθερώνουμε όλους τους κόμβους της λίστας μας με τη χρήση ενός loop, διατρέχοντάς την από την αρχή μέχρι το τέλος της. Έχω σκοπό να γράψω σε επόμενο ποστ αναλυτικά σχόλια και τον κώδικα συνάρτησης που αποδεσμεύει τη μνήμη όλων των κόμβων μιας λίστας.
4. Μετά την απελευθέρωση των κόμβων έχεις μια συνθήκη if-else προκειμένου να ελέγξεις αν πέτυχε η απελευθέρωση μνήμης....
- Κώδικας: Επιλογή όλων
if (head->id) // <-- λάθος
printf("H free dn ekane kala tin douleia tis \n");
else
printf("H mnimi eleutherwthike!");
Εδώ υπάρχουν 2 προβλήματα, το σοβαρότερο εκ των οποίων είναι πως και πάλι προσπαθείς να χρησιμοποιήσεις έναν κατεστραμμένο κόμβο (από το προηγούμενο free) και συγκεκριμένα προσπαθείς να χρησιμοποιήσεις το πεδίο: id του κατεστραμμένου κόμβου (head->id). Το επόμενο πρόβλημα είναι πως το πεδίο id είναι τύπου int, οπότε ακόμα και αν ο κόμβος head υπήρχε, η συνθήκη: if ( head->id ) συγκρίνει την int τιμή του id με το 0 για να αποφασίσει αν απελευθερώθηκε ή όχι η μνήμη;
Υποθέτω πως εννοούσες: if (head == NULL). Πάντως δεν είναι καλή ιδέα να συγκρίνεις τον δείκτη ενός κόμβου που έχεις ήδη καταστρέψει με το free(). Προφανώς ούτε να τον χρησιμοποιείς. Για να τον ξαναχρησιμοποιήσει ως κόμβο θα πρέπει να κάνεις εκ νέου malloc()/calloc().
Να πω και κάτι τελευταίο, που έχω την εντύπωση πως δεν έχεις ξεκαθαρίσει στο μυαλό σου. Μπορεί να κάνω και λάθος, αλλά θα το πω καλού-κακού μιας και το φόρουμ το διαβάζει πολύς κόσμος.
Έχει να κάνει με τον συμβολισμό -> για πρόσβαση στα πεδία μιας μεταβλητής που είναι τύπου struct. Ο κανονικός συμβολισμός για να αναφερθούμε σε ένα πεδίο μιας μεταβλητής τύπου struct είναι η τελεία (.).
Έστω...
- Κώδικας: Επιλογή όλων
struct coordinates {
int x;
int y;
};
struct coordinates pos; // position
Για να βάλουμε τιμές στα πεδία x και y της μεταβλητής pos, γράφουμε...
- Κώδικας: Επιλογή όλων
pos.x = 100;
pos.y = 200;
Αν όμως την μεταβλητή την ορίσουμε να είναι δείκτης, τότε αντί για τελεία χρησιμοποιούμε -> ...
- Κώδικας: Επιλογή όλων
struct coordinates {
int x;
int y;
};
struct coordinates *pos; // position
pos = calloc(1, sizeof( struct coordinates) );
// εδώ κανονικά θέλει κι έλεγχο αν πέτυχε το calloc()... if (pos != NULL)
pos->x = 100;
pos->y = 200;
...
free( pos );
Άρα λοιπόν, στις λίστες (και οπουδήποτε) το σύμβολο -> δεν δείχνει στον επόμενο κόμβο, αλλά αναφέρεται σε ένα πεδίο του τρέχοντος κόμβου
Το παραπάνω παράδειγμα είναι ιδανικό για να ξεκαθαρίσει κανείς κι ένα ακόμα σημείο: πως όταν δήλωσα τη μεταβλητή pos ως δείκτη ΔΕΝ επιχείρησα να περάσω απευθείας τιμές στα πεδία της (κάτι τέτοιο θα μου έδινε segmentation fault). Πρώτα έφτιαξα χώρο στη μνήμη, βάζοντας τον δείκτη pos να δείχνει σε εκείνο το χώρο (pos = calloc( ... ) )
και ΜΕΤΑ έβαλα τιμές στα πεδία του, με τον συμβολισμό των δεικτών ( -> ).
Το ίδιο συμβαίνει και με τις λίστες (και παντού). Ο δείκτης head ΔΕΝ είναι κόμβος ο ίδιος, είναι ένας δείκτης που δείχνει σε έναν κόμβο (στον 1ο της λίστας εν προκειμένω) που πρέπει πρώτα να έχει δημιουργηθεί στη μνήμη. Αυτό το κάνει πιο ξεκάθαρο η 2η από τις εικόνες που πόσταρα στο προηγούμενο post.
Είναι σημαντικό να είναι ξεκάθαρο αυτό το σημείο!
Εκτος και αν εχουμε ορισει εμεις στον κομβο να δειχνει στο επομενο στοιχειο της λιστας ετσι δεν ειναι?????
ΑΝτε τελειωσα τωρα αυτο το κομματι σου! Τα πηγαινω σιγα σιγα... μπραβο migf1 ευχαριστουμε και παλι για ολα.