7. Δείκτες
Σύνοψη Διευθύνσεις μνήμης μεταβλητών, μεταβλητές δείκτη, δείκτες και συναρτήσεις, κλήση με αναφορά, επιστροφή πολλών τιμών από συναρτήσεις μέσω δεικτών, δείκτες σε συναρτήσεις, πίνακες και δείκτες, δείκτες σε δείκτες, πίνακες ως ορίσματα συναρτήσεων, δείκτες και αλφαριθμητικά, δείκτες σε δομές.
Προαπαιτούμενη γνώση Τύποι δεδομένων, είσοδος/έξοδος, δομές επιλογής και επανάληψης, συναρτήσεις, πίνακες, δομές.
7.1 Εμφάνιση διευθύνσεων μνήμης μεταβλητών
Κάθε μεταβλητή στη γλώσσα C βρίσκεται σε συγκεκριμένη θέση στη μνήμη του υπολογιστή. Αυτή η θέση είναι ένας ακέραιος αριθμός αυστηρά θετικός. Στην ουσία στον προγραμματισμό κάθε αναφορά σε μεταβλητή πάντοτε μεταφράζεται σε διευθύνσεις στη μνήμη. Η θέση αυτή ονομάζεται διεύθυνση και υπάρχει δυνατότητα να εντοπιστεί με τη χρήση του τελεστή & όπως παρουσιάζεται και στον κώδικα 7.1. Σε αυτό το παράδειγμα στην πρώτη γραμμή εκτύπωσης εμφανίζονται οι τιμές των μεταβλητών x και y (οι τιμές 100 και 200 αντίστοιχα) και στη δεύτερη γραμμή οι θέσεις τους στη μνήμη του υπολογιστή. Παρατηρήστε ότι για να εκτυπωθούν οι διευθύνσεις μνήμης χρησιμοποιήθηκε ο προσδιοριστής διαμόρφωσης %p και οι μεταβλητές που περιέχουν διευθύνσεις μνήμης μετατρέπονται σε void* καθώς περνούν ως ορίσματα στην printf().
Κώδικας 7.1: ch7_p1.c - εμφάνιση διευθύνσεων μνήμης που καταλαμβάνουν δύο μεταβλητές. | |
---|---|
Στη συνέχεια παρουσιάζονται δύο διαφορετικές εκτελέσεις του ίδιου προγράμματος.
Ένα ενδιαφέρον στοιχείο αυτού του μικρού προγράμματος είναι ότι δεν εκτυπώνει πάντα τις ίδιες διευθύνσεις, όπως παρατηρείται και στο παράδειγμα εκτέλεσης. Αυτό συμβαίνει καθώς ο μεταγλωττιστής δεν δεσμεύει πάντοτε την ίδια διεύθυνση μνήμης για κάθε μεταβλητή.
7.2 Μεταβλητές δείκτη
Οι διευθύνσεις μνήμης μπορούν να εκχωρηθούν σε μεταβλητές ειδικού σκοπού που ονομάζονται δείκτες. Οι μεταβλητές αυτές δηλώνονται ως TYPE *VARIABLE, όπου TYPE ένας οποιοσδήποτε τύπος της γλώσσας (π.χ. int, double κλπ.). Για παράδειγμα η δήλωση:
δημιουργεί μια μεταβλητή δείκτη που δείχνει σε διευθύνσεις όπου θα αποθηκευτούν ακέραιες τιμές. Από την άλλη η δήλωση: δημιουργεί μια μεταβλητή δείκτη που δείχνει σε διευθύνσεις όπου μπορούν να τοποθετηθούν τιμές με δεκαδικά ψηφία. Ωστόσο, αν και οι δύο δείκτες έχουν διαφορετικό βασικό τύπο (int και double αντίστοιχα), στην πράξη και ο ένας δείκτης αλλά και ο άλλος θα έχουν το ίδιο μέγεθος σε bytes και θα μπορούν και οι δύο να αποθηκεύσουν διευθύνσεις. Αυτό φαίνεται και στο παράδειγμα του κώδικα 7.2, όπου εμφανίζεται στην οθόνη το μέγεθος σε bytes των δεικτών για δύο μεταβλητές διαφορετικού τύπου.Στο παράδειγμα του κώδικα 7.3 γίνεται ανάθεση διευθύνσεων μεταβλητών σε δείκτες και στη συνέχεια γίνεται εμφάνιση αυτών των διευθύνσεων.
Κώδικας 7.3: ch7_p3.c - ανάθεση διευθύνσεων μεταβλητών σε δείκτες. | |
---|---|
Κώδικας 7.4: ch7_p4.c - αποαναφορά δείκτη. | |
---|---|
Η εκτέλεση του προγράμματος εμφανίζει:
7.3 Δείκτες και συναρτήσεις
7.3.1 Κλήση με αναφορά
Οι συναρτήσεις στη γλώσσα C τυπικά δέχονται ορίσματα με τιμή, δηλαδή απλά γίνεται ανάθεση της τιμής που περνά η καλούσα συνάρτηση στο όρισμα και τίποτα παραπάνω. Έστω το παράδειγμα του κώδικα 7.5.
Κώδικας 7.5: ch7_p5.c - κλήση με τιμή (call by value). | |
---|---|
- Nα επιστρέφει την τιμή με return.
- Να γίνει κλήση με αναφορά.
Στην πρώτη περίπτωση θα πρέπει η συνάρτηση να έχει την εντολή return x ως τελευταία εντολή έτσι ώστε επιστραφεί η τιμή στη main(). Σε αυτήν την περίπτωση το πρόγραμμα θα λάβει τη μορφή που παρουσιάζεται στον κώδικα 7.6.
Κώδικας 7.6: ch7_p6.c - αλλαγή της τιμής της μεταβλητής mainX με εκχώρηση της τιμής που επιστρέφει η συνάρτηση f() στη mainX. | |
---|---|
Η συνάρτηση f() επιστρέφει το όρισμα x αλλαγμένο, επομένως και η main() θα μπορέσει να το λάβει με επιστροφή τιμής. Η εκτέλεση του παραπάνω προγράμματος παρουσιάζεται στη συνέχεια:
Δεν είναι πάντα εφικτό να χρησιμοποιείται η τιμή επιστροφής μιας συνάρτησης για την αλλαγή της τιμής ενός ορίσματός της, επομένως πρέπει να βρεθεί κάποιος εναλλακτικός τρόπος επιστροφής τιμής από συνάρτηση. Σε αυτόν τον εναλλακτικό τρόπο, αντί να περνά η τιμή μιας μεταβλητής σε μια συνάρτηση, περνά ένας δείκτης προς τη διεύθυνση της μεταβλητής και η αλλαγή στην τιμή της μεταβλητής γίνεται με αποαναφορά. Αυτό το είδος περάσματος παραμέτρων ονομάζεται κλήση με αναφορά (call by reference). Η νέα μορφή του κώδικα παρουσιάζεται στον κώδικα 7.7. Σε αυτήν την περίπτωση η συνάρτηση f() δέχεται ως όρισμα έναν δείκτη σε ακέραιο και όχι έναν ακέραιο όπως πριν. Ωστόσο, για να μπορέσει να αλλάξει την τιμή που δείχνει ο δείκτης πρέπει να γίνει αποαναφορά. Μετά την ολοκλήρωση της εκτέλεσης της συνάρτησης, η τιμή της mainX θα έχει αλλάξει επίσης, καθώς η συνάρτηση f() θα έχει αλλάξει το περιεχόμενο που έδειχνε ο δείκτης x και όχι τον ίδιο τον δείκτη.
Κώδικας 7.7: ch7_p7.c - αλλαγή της τιμής της μεταβλητής mainX μέσα στη συνάρτηση f() με αποαναφορά. | |
---|---|
H εκτέλεση του προγράμματος με τη νέα αλλαγή θα εμφανίσει:
7.3.2 Επιστροφή πολλών τιμών από συνάρτηση
Με τη χρήση των δεικτών και τη δυνατότητα που δίνουν για έμμεση αναφορά σε μεταβλητές γίνεται εφικτή η επιστροφή παραπάνω από μια τιμών από μια συνάρτηση. Για παράδειγμα έστω ότι είναι επιθυμητό μια συνάρτηση να επιστρέφει μια τιμή από έναν υπολογισμό. Αυτό συμβαίνει στον κώδικα 7.8 όπου η συνάρτηση calc_min() δέχεται ως όρισμα μια ακέραια τιμή n, διαβάζει από το πληκτρολόγιο n αριθμούς και επιστρέφει τον μικρότερο από αυτούς.
Κώδικας 7.8: ch7_p8.c - παράδειγμα συνάρτησης που επιστρέφει μια τιμή. | |
---|---|
Ωστόσο, αν είναι επιθυμητό η συνάρτηση να επιστρέψει ταυτόχρονα και το μέγιστο και το ελάχιστο των n αριθμών, αυτό σημαίνει ότι θα πρέπει να οριστεί μια δομή με πεδία τα δύο αποτελέσματα και να επιστρέφεται από τη συνάρτηση μια εγγραφή αυτής της δομής. Ένας απλούστερος τρόπος είναι να πραγματοποιηθεί κλήση με αναφορά και η συνάρτηση να δεχθεί ως ορίσματα δύο δείκτες, με τον πρώτο να δείχνει στην ελάχιστη τιμή του πίνακα και τον δεύτερο στη μέγιστη τιμή, όπως συμβαίνει στον κώδικα 7.9. Ο δείκτης min δείχνει προς την ελάχιστη τιμή και ο δείκτης max στη μέγιστη. Για να επιστραφούν αυτές οι τιμές με αλλαγές, χρειάζεται να γίνει αποαναφορά δεικτών.
Κώδικας 7.10: ch7_p10.c - λανθασμένος κώδικας συνάρτησης αντιμετάθεσης μεταβλητών. | |
---|---|
Κώδικας 7.11: ch7_p11.c - συνάρτηση που αντιμεταθέτει δύο ακέραιες μεταβλητές. | |
---|---|
7.3.3 Δείκτες σε συναρτήσεις
Ένας δείκτης σε συνάρτηση (function pointer) είναι μια ειδική περίπτωση δείκτη όπου ο δείκτης δεν δείχνει σε μια θέση μνήμης που περιέχει μια μεταβλητή, αλλά στο σημείο εκκίνησης μίας συνάρτησης. Ένα παράδειγμα που επιδεικνύει τη χρήση δεικτών σε συναρτήσεις βρίσκεται στον κώδικα 7.12. Στο παράδειγμα αυτό δημιουργείται ένας δείκτης σε συνάρτηση που δέχεται ως ορίσματα δύο ακέραιες τιμές και επιστρέφει μια ακέραια τιμή. Έτσι με έμμεση αναφορά στην αντίστοιχη συνάρτηση μπορεί να καλείται είτε η συνάρτηση add() είτε η συνάρτηση sub().
Ακολουθεί το αποτέλεσμα της εκτέλεσης του κώδικα:
7.4 Πίνακες και δείκτες
Στη γλώσσα C υπάρχει μια σύνδεση ανάμεσα στους πίνακες και τους δείκτες και γενικά κάθε αναφορά σε δείκτη μπορεί να θεωρηθεί και αναφορά σε πίνακα όπως και το αντίστροφο. Για παράδειγμα οι δηλώσεις:
δημιουργούν έναν πίνακα ακεραίων με 5 συνεχόμενα στοιχεία στη μνήμη του υπολογιστή και αναθέτουν στον δείκτη px τη διεύθυνση του πρώτου στοιχείου του πίνακα. Στα στοιχεία του πίνακα x μπορεί να γίνει αναφορά με τον γνωστό τρόπο, δηλαδή x[0], x[1], x[2], x[3], x[4] αλλά και με χρήση του δείκτη px, όπως φαίνεται και στον κώδικα 7.13.
Η αναφορά *(px+1) αφορά το δεύτερο στοιχείο του πίνακα και η αναφορά *(px+2) το τρίτο στοιχείο του πίνακα. Η έξοδος του προγράμματος είναι:
x[0]=1 x[1]=10 x[2]=100 x[3]=1000 x[4]=10000
*(0x16d6f2e14)=1 *(0x16d6f2e18)=10 *(0x16d6f2e1c)=100 *(0x16d6f2e20)=1000
↪ *(0x16d6f2e24)=10000
Οι δείκτες, αν και είναι κατά βάση ακέραιες μεταβλητές, αναφέρονται σε θέσεις μνήμης υπολογιστή και επομένως θα πρέπει να διαθέτουν και αντίστοιχους τελεστές. Στο παράδειγμα του κώδικα 7.14, παρουσιάζεται μια διαφορετική χρήση των μοναδιαίων τελεστών αύξησης και μείωσης. Στο παράδειγμα αυτό χρησιμοποιείται μια μεταβλητή χαρακτήρα, ένας δείκτης στη μεταβλητή χαρακτήρα, μια ακέραια μεταβλητή και ένας δείκτης στην ακέραια μεταβλητή. Οι μοναδιαίοι τελεστές ++ και ‐‐ δεν επιδρούν πάνω στις τιμές των μεταβλητών αλλά στις διευθύνσεις που δείχνουν οι δείκτες.
c=A i=100 p2c=0x16bd42e2b p2i=0x16bd42e1c
c=A i=100 p2c=0x16bd42e2c p2i=0x16bd42e20
c=A i=100 p2c=0x16bd42e2c p2i=0x16bd42e1c
Οι δείκτες μπορούν να χρησιμοποιηθούν για τη διάσχιση πίνακα μέσω των τελεστών μοναδιαίας αύξησης και μείωσης. Στο παράδειγμα του κώδικα 7.15 σκοπός είναι να βρεθεί στον πίνακα x η θέση της τιμής που περιέχει η μεταβλητή element. Για την αναζήτηση χρησιμοποιείται δείκτης που δείχνει αρχικά στο πρώτο στοιχείο του πίνακα. Σε κάθε επανάληψη, όσο η τιμή δεν έχει ακόμα εντοπιστεί, ο δείκτης πηγαίνει στο επόμενο στοιχείο του πίνακα με χρήση του μοναδιαίου τελεστή ++. Σε κάθε επανάληψη ο δείκτης px μεταφέρεται στη διεύθυνση του επόμενου στοιχείου του πίνακα και με αποαναφορά γίνεται έλεγχος σχετικά με το εάν ο πίνακας περιέχει τη ζητούμενη τιμή.
7.4.1 Δείκτης σε δείκτη
Ένας δείκτης μπορεί να δείχνει και σε συνθετότερες δομές, όπως για παράδειγμα σε πίνακες, σε δομές, σε ενώσεις ή και σε άλλους δείκτες. Στο παράδειγμα του κώδικα 7.16 δημιουργείται ένας υποπίνακας από έναν αρχικό πίνακα.
Κώδικας 7.16: ch7_p16.c - «λήψη» ενός υποπίνακα με χρήση δεικτών. | |
---|---|
Η έξοδος του παραπάνω προγράμματος είναι:
Επιπλέον, ένας δείκτης μπορεί να δείξει και σε άλλο δείκτη, όπως φαίνεται και στο παράδειγμα του κώδικα 7.17.
Ο δείκτης pt1 δείχνει στη διεύθυνση της ακέραιας μεταβλητής var και ο δείκτης pt2 δείχνει στη διεύθυνση του δείκτη pt1. Μετά την εκτέλεση του προγράμματος, μια ενδεικτική εκτύπωση θα είναι η ακόλουθη:
1: var=100 pt1=0x16d6e2e28 pt2=0x16d6e2e20
2: var=101 pt1=0x16d6e2e28 pt2=0x16d6e2e20
3: var=102 pt1=0x16d6e2e28 pt2=0x16d6e2e20
Η πρώτη γραμμή της εξόδου εμφανίζει την τιμή της μεταβλητής και τις διευθύνσεις που έχουν οι δείκτες. Η αποαναφορά *pt1 επιστρέφει την τιμή 100 όπως είναι άλλωστε και η τιμή της var. Την ίδια τιμή θα επέστρεφε και η αποαναφορά του διπλού δείκτη **pt2. Συνεπώς, οι εντολές (*pt1)++ και (**pt2)++ προκαλούν αύξηση της τιμής που διατηρεί η μεταβλητή var κατά ένα και έτσι προκύπτει η έξοδος στη δεύτερη και τρίτη γραμμή της εξόδου.
Μια χρησιμότητα των δεικτών σε δείκτη εντοπίζεται στις περιπτώσεις συναρτήσεων που είναι επιθυμητή η αλλαγή ενός δείκτη και όχι του περιεχομένου του, όπως παρουσιάζεται στο παράδειγμα του κώδικα 7.18 όπου η συνάρτηση try1() και η συνάρτηση try2() στοχεύουν στο να αλλάξουν έμμεσα την τιμή που βρίσκεται στη θέση 3 του πίνακα.
pt1=0x16b5df068 , pt2=0x16b5df068
BEFORE x=0x16b5df068
AFTER x=0x16b5df06c
0x16b5df060=10 0x16b5df064=20 0x16b5df068=31 0x16b5df06c=40 0x16b5df070=50
0x16b5df060=10 0x16b5df064=20 0x16b5df068=31 0x16b5df06c=41 0x16b5df070=50
pt1=0x16b5df068 , pt2=0x16b5df06c
7.4.2 Πέρασμα πινάκων σε συναρτήσεις
Οι δείκτες μπορούν να χρησιμοποιηθούν για το πέρασμα πινάκων σε συναρτήσεις. Στο παράδειγμα του κώδικα 7.19, τα στοιχεία ενός πίνακα αλλάζουν μέσα σε μια συνάρτηση.
Μια συνάρτηση μπορεί να χρησιμοποιηθεί ώστε να επιστρέψει πολλαπλές τιμές ως πεδία ενός πίνακα. Για παράδειγμα αν θέλουμε μια συνάρτηση να επιστρέφει την ελάχιστη τιμή, τη μέγιστη τιμή και τον μέσο όρο των τιμών ενός πίνακα, αυτό μπορεί να γίνει με χρήση 3 δεικτών, έναν για κάθε αποτέλεσμα. Ωστόσο, το ίδιο μπορεί να γίνει και με πίνακα όπως παρουσιάζεται στον κώδικα 7.21. Στη συνάρτηση get_stats() το στοιχείο του πίνακα stats[0] χρησιμοποιείται για την αποθήκευση της ελάχιστης τιμής, το stats[1] για αποθήκευση της μέγιστης τιμής και το stats[2] για αποθήκευση του μέσου όρου.
Ωστόσο, η ανάγκη απομνημόνευσης της σειράς που είναι αποθηκευμένα τα αποτελέσματα στον πίνακα προκαλεί δυσκολία στην κατανόηση του κώδικα. Στον κώδικα 7.22 παρουσιάζεται μια ισοδύναμη υλοποίηση που χρησιμοποιεί κατάλληλα ονοματισμένους δείκτες τοπικής εμβέλειας. Η ανάθεση τιμών στους βοηθητικούς δείκτες πραγματοποιείται στη γραμμή 6 του κώδικα.
7.4.3 Δείκτες και αλφαριθμητικά
Τα αλφαριθμητικά είναι και αυτά πίνακες και επομένως όσα έχουν αναφερθεί μέχρι στιγμής για τη σχέση δεικτών και πινάκων ισχύουν και για τα αλφαριθμητικά με την επιπλέον σημείωση πως στο τέλος των αλφαριθμητικών πρέπει να υπάρχει πάντα ο χαρακτήρας τερματισμού '\0'. Στο παράδειγμα του κώδικα 7.23 πραγματοποιείται αρχικοποίηση αλφαριθμητικών με χρήση δεικτών. Το αλφαριθμητικό s1 είναι στατικό και η ανάθεση τιμής σε αυτό γίνεται στη συνέχεια με τη συνάρτηση strcpy(). Το αλφαριθμητικό s2 αρχικοποιείται με ανάθεση τιμής κατά τη δήλωσή του. Το αλφαριθμητικό s3 είναι δείκτης που δείχνει σε μια δεσμευμένη περιοχή μνήμης όπου υπάρχουν οι χαρακτήρες της ακολουθίας.
Κώδικας 7.23: ch7_p23.c - αναθέσεις τιμών σε αλφαριθμητικά. | |
---|---|
Οι δείκτες μπορούν να χρησιμοποιηθούν και για το πέρασμα αλφαριθμητικών ως ορισμάτων σε συναρτήσεις. Στον κώδικα 7.24 η συνάρτηση find_digits() επιστρέφει το πλήθος των χαρακτήρων ενός αλφαριθμητικού που είναι ψηφία. Η διαφορά με τις συναρτήσεις με ορίσματα πίνακες είναι πως στα αλφαριθμητικά δεν χρειάζεται να περάσει ως όρισμα το μήκος του αλφαριθμητικού, αφού αυτό υποδηλώνεται από το '\0' ή εναλλακτικά μπορεί να υπολογιστεί με τη συνάρτηση strlen() του string.h.
Κώδικας 7.24: ch7_p24.c - εύρεση του πλήθους των ψηφίων που βρίσκονται σε ένα αλφαριθμητικό. | |
---|---|
Ισοδύναμη συνάρτηση με τη strlen() μπορεί να γραφεί εύκολα κάνοντας χρήση αριθμητικής δεικτών, όπως φαίνεται στον κώδικα 7.25.
Κώδικας 7.25: ch7_p25.c - συνάρτηση εύρεσης του μήκους ενός αλφαριθμητικού. | |
---|---|
7.5 Δείκτης σε δομή
Ένας δείκτης μπορεί να χρησιμοποιηθεί ακόμα και για να δείξει σε μια μεταβλητή δομής. Στο παράδειγμα του κώδικα 7.26 ένας δείκτης δείχνει σε μια δομή για πρόσωπα και με αποαναφορά πραγματοποιεί τροποποίηση των στοιχείων της δομής. Η αποαναφορά ενός δείκτη προς μία δομή προκειμένου να πραγματοποιηθεί πρόσβαση σε ένα πεδίο της δομής γίνεται με δύο τρόπους: με τον τελεστή τελείας, όπως στο παράδειγμα (*pt).name και εναλλακτικά με τον τελεστή ‐> όπως στο παράδειγμα pt‐>name. Και στις δύο περιπτώσεις που αναφέρθηκαν γίνεται αποαναφορά του δείκτη για πρόσβαση στο πεδίο name της δομής.
Μια ενδεικτική εκτέλεση του προγράμματος δίνει τα ακόλουθα αποτελέσματα:
The pointer 0x16b0a2d4c points to a 'person'
Each 'person' occupies 204 bytes
Id: 1000, name: Ioannis , last name: Pappas
7.6 Δείκτες και πίνακες
Οι πίνακες και οι δείκτες είναι σχετικές μεταξύ τους έννοιες άλλα όχι ταυτόσημες. Το όνομα ενός πίνακα είναι η διεύθυνση προς το πρώτο στοιχείο του πίνακα. Αυτό σημαίνει ότι, για παράδειγμα, για έναν πίνακα με όνομα a, πέντε ακεραίων, το a δείχνει στο πρώτο στοιχείο του πίνακα, δηλαδή στο a[0]. Συνεπώς, οι δύο τρόποι διάσχισης ενός πίνακα που παρουσιάζονται στον κώδικα 7.27 επιτυγχάνουν το ίδιο αποτέλεσμα.
Η έξοδος του προγράμματος είναι η ακόλουθη:
Ωστόσο, οι πίνακες και οι δείκτες έχουν και διαφορές. Για παράδειγμα η χρήση του τελεστή sizeof σε δείκτη θα επιστρέψει το πλήθος των bytes που απαιτούνται για διευθυνσιοδότηση της μνήμης (π.χ. 8 bytes σε συστήματα αρχιτεκτονικής 64 bit), ενώ για έναν πίνακα θα επιστρέψει το συνολικό μέγεθος σε bytes του πίνακα. Ο κώδικας 7.28 δείχνει αυτήν τη διαφορά και ότι μπορεί να γίνει διάσχιση ενός πίνακα χρησιμοποιώντας έναν δείκτη προς το πρώτο στοιχείο του πίνακα και αριθμητική δεικτών για μετακίνηση από ένα στοιχείο του πίνακα στο επόμενο.
Η έξοδος του προγράμματος είναι η ακόλουθη:
Size of array (arr) = 20 bytes
Size of pointer (ptr) = 8 bytes
Using array indexing:
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
Using pointer:
*(ptr + 0) = 1
*(ptr + 1) = 2
*(ptr + 2) = 3
*(ptr + 3) = 4
*(ptr + 4) = 5
After incrementing pointer:
First element pointed by ptr = 2
First element of array = 1
Επίσης, στη γραμμή 20 έχει τοποθετηθεί σε σχόλια μια εντολή αλλαγής της διεύθυνσης που περιέχει το όνομα του πίνακα, κάτι το οποίο δεν επιτρέπεται. Αν η γραμμή 20 συμπεριληφθεί χωρίς σχόλιο, τότε ο μεταγλωττιστής θα εμφανίσει ένα μήνυμα λάθους της μορφής:
ch7_p28.c:24:8: error: cannot increment value of type 'int[5]'
arr++; // Δεν γίνεται δεκτό από τον μεταγλωττιστή
~~~^
1 error generated.
Ένα ακόμα λεπτό σημείο που αφορά τη σχέση δεικτών και πινάκων είναι το λεγόμενο “decay to pointer” («αποσύνθεση» πίνακα σε δείκτη) και έχει να κάνει με το πέρασμα πινάκων σε συναρτήσεις. Ο κώδικας 7.29 δείχνει ότι στη main() το μέγεθος του πίνακα είναι το πραγματικό μέγεθός του σε bytes. Ωστόσο, μόλις o πίνακας περάσει ως όρισμα στη συνάρτηση print_array_size(), το μέγεθος του πίνακα πλέον είναι το μέγεθος ενός δείκτη. Αυτό δείχνει πως ο πίνακας «αποσυντίθεται» σε δείκτη όταν περνά ως όρισμα σε μια συνάρτηση.
Outside function: Array size = 20 bytes
Inside function: Array size = 8 bytes
Array elements: 1 2 3 4 5
7.7 Ασκήσεις
Άσκηση 1
Να γραφεί συνάρτηση int last_found(int *x, int n, int element) που βρίσκει και επιστρέφει την τελευταία εμφάνιση του element στον πίνακα n στοιχείων, x. Αν το στοιχείο element δεν βρεθεί τότε η συνάρτηση να επιστρέφει την τιμή -1
Λύση άσκησης 1
Άσκηση 2
Nα γραφεί συνάρτηση int from_binary(char *s). Η συνάρτηση να λαμβάνει ως όρισμα το αλφαριθμητικό s που μπορεί να θεωρηθεί ότι περιέχει μόνο δυαδικά ψηφία (0,1) και να επιστρέφει την ισοδύναμη τιμή του στο δεκαδικό σύστημα, π.χ. αν s="1110", θα πρέπει να επιστρέψει 14.
Λύση άσκησης 2
Άσκηση 3
Γράψτε μια συνάρτηση με όνομα reverse_array που να αντιστρέφει ένα τμήμα ενός πίνακα. Η συνάρτηση να έχει ως πρωτότυπο το void reverse_array(int* start, int* end) και να αντιστρέφει το τμήμα του πίνακα από το στοιχείο του πίνακα στο οποίο δείχνει ο δείκτης start μέχρι και το στοιχείο που δείχνει ο δείκτης end. Γράψτε ένα πρόγραμμα που να επιδεικνύει τη λειτουργία της συνάρτησης εκτυπώνοντας έναν πίνακα πριν και μετά την κλήση της.
Λύση άσκησης 3
Άσκηση 4
Κατασκευάστε ένα πρόγραμμα που διαχειρίζεται τις βαθμολογίες των φοιτητών σε ένα μάθημα. Για κάθε φοιτητή, θα πρέπει να αποθηκεύεται το όνομά του, ο αριθμός μητρώου και ο βαθμός του στο μάθημα. Ορίστε μια δομή student που θα περιέχει τις παραπάνω πληροφορίες. Ορίστε μια δομή course που θα περιέχει έναν πίνακα (με μέγιστο μέγεθος 100) από εγγραφές της δομής student, και το πλήθος των φοιτητών που έχουν λάβει βαθμολογία στο μάθημα. Προσθέστε τις εξής λειτουργίες:
- Προσθήκη βαθμολογίας για έναν φοιτητή.
- Αφαίρεση βαθμολογίας φοιτητή με βάση έναν αριθμό μητρώου. Αν ο αριθμός μητρώου βρεθεί τότε ο βαθμός του φοιτητή να γίνεται μηδέν.
- Εκτύπωση όλων των βαθμολογιών των φοιτητών του μαθήματος.