Τεχνολογικό Εκπαιδευτικό Ίδρυμα Κρήτης Σχολή Εφαρμοσμένων Επιστημών Τμήμα Ηλεκτρονικών Μηχανικών Τομέας Αυτοματισμού και Πληροφορικής Δομημένος Προγραμματισμός (ΤΛ1006) Δρ. Μηχ. Νικόλαος Πετράκης, Καθηγητής Εφαρμογών (npet@chania.teicrete.gr) Ένατη (9 η ) τρίωρη διάλεξη. Ιστοσελίδα Μαθήματος: https://eclass.chania.teicrete.gr/courses/el106 Εξάμηνο: Χειμερινό 2018-19
Ακόμα ένα παράδειγμα Να γίνει ένα πρόγραμμα το οποίο να διαβάζει ένα αρχείο κειμένου (πρότυπη είσοδο), γραμμή προς γραμμή, και να εμφανίζει στην οθόνη (πρότυπη έξοδο) την γραμμή που διάβασε μόνο εφόσον περιέχει κάποιο συγκεκριμένο συνδυασμό χαρακτήρων, ενώ όταν φτάσει στο τέλος του αρχείου να εμφανίζει το πλήθος των γραμμών που περιείχαν τον ζητούμενο συνδυασμό. Περίγραμμα προγράμματος ενόσω (υπάρχει άλλη γραμμή, διάβασέ την) εάν (η γραμμή περιέχει τον συνδυασμό) εμφάνισέ την αύξησε τον μετρητή κατά ένα εμφάνισε τον συνολικό αριθμό γραμμών που βρέθηκαν να περιέχουν το συνδυασμό. Θα βρείτε την λύση, που δόθηκε στον πίνακα, στις σημειώσεις της θεωρίας. 2
Τυπικά ορίσματα με const Όπως είπαμε, όταν ένα όρισμα διοχετεύεται σε μία συνάρτηση (και δεν είναι δείκτης διεύθυνσης) τότε η τιμή του αντιγράφεται σε μία προσωρινή μεταβλητή, και χρησιμοποιείται στο σώμα της συνάρτησης. Όμως, όταν διοχετεύουμε μία διάταξη σαν όρισμα τότε ουσιαστικά περνάμε ένα δείκτη διεύθυνσης. Σε μερικές όμως περιπτώσεις, θέλουμε να είμαστε σίγουροι ότι η διάταξη δεν θα αλλαχθεί από την συνάρτηση. Τότε χρησιμοποιούμε τη λέξη κλειδί const στις παραμέτρους της συνάρτησης. 3
Παράδειγμα void init_zeros (int a[ ], int size) { int i; for (i = 0; i < size; i++) a[i] = 0; void use_array(const int a[ ], int size,...) { /* Ο μεταγλωττιστής ελέγχει εάν αλλάζουμε την τιμή της διάταξης a */ Δείκτης σε int, αναπαριστά ένα δείκτη σε μία διάταξη. Θα το δείτε κάποιες φορές ως *a 4
Δείκτες διεύθυνσης και πίνακες Απλά ας θυμηθούμε ότι οι πολυδιάστατοι πίνακες (διατάξεις) στη C καταχωρίζονται στη μνήμη σαν μία γραμμή (η μία γραμμή μετά την άλλη). Σημαντικό: Για πίνακες, ως όρισμα περνάει ο δείκτης στο πρώτο στοιχείο a[0] (ή a[0][0] ή a[0][0][0]). Δηλαδή, το πρωτότυπο συνάρτησης μπορεί να είναι: int find_largest(int x[], int m); και η κλήση της συνάρτησης: find_largest(a, NR_COLS * NR_ROWS); 5
Παράδειγμα #define NR_ROWS 10 #define NR_COLS 15... int a[nr_rows][nr_cols]; int row, col; //θα μπορούσε να ήταν i και j... for(row = 0; row < NR_ROWS; row++) for(col = 0; col < NR_COLS; col++) a[row][col] = 0; ή εναλλακτικά: int *p; for(p=&a[0][0]; p<=&a[nr_rows-1][nr_cols-1]; p++) *p = 0; 6
Χαρακτήρες και συμβολοσειρές Ένας χαρακτήρας είναι ένα γράμμα, ένα σημείο στίξης ή ένα σύμβολο. Η μνήμη του υπολογιστή αποθηκεύει όλα τα δεδομένα σε αριθμητική μορφή. Δεν υπάρχει άμεσος τρόπος αποθήκευσης χαρακτήρων, υπάρχει όμως ένας αριθμητικός κώδικας για αποθήκευση χαρακτήρων, ο κώδικας ASCII (0-255). Για παράδειγμα το γράμμα 'a' έχει κωδικό ASCII = 97. Π.χ. #define EX 'x' char a, b = ΕΧ, c = 'y'; c = '!'; c = EX; 7
Χαρακτήρες και συμβολοσειρές Στη C, μία συμβολοσειρά ορίζεται ως μία ακολουθία χαρακτήρων που τελειώνει με τον μηδενικό χαρακτήρα, που αναπαρίσταται από το '\0' (με τιμή ASCII = 0). Οι διατάξεις τύπου char δηλώνονται όπως όλες οι διατάξεις, π.χ.: char name[10]; Εδώ μπορούμε να αποθηκεύσουμε μία συμβολοσειρά μήκους μέχρι 9 χαρακτήρων (10 μαζί με τον μηδενικό χαρακτήρα). Αρχικοποίηση συμβολοσειρών char name[10] = {'a', 'l', 'a', 'b', 'a', 'm', 'a', '\0' ; char name[10] = "Alabama"; // Ο χαρακτήρας ' \0 ' προστίθεται αυτόματα Ο μηδενικός χαρακτήρας καθορίζει το τέλος της συμβολοσειράς χωρίς αυτόν ο μεταγλωττιστής δεν έχει τρόπο να βρει το τέλος της συμβολοσειράς γι αυτό προχωράει μέχρι να συναντήσει κάποιον μηδενικό χαρακτήρα στη μνήμη. 8
Συμβολοσειρές και δείκτες διεύθυνσης Επειδή υπάρχει σήμανση για το τέλος μίας συμβολοσειράς το μόνο που χρειαζόμαστε για να ορίσουμε τη συμβολοσειρά είναι κάτι που να δείχνει την αρχή της. Άρα χρειάζεται μόνο το όνομα της συμβολοσειράς ή διάταξης χαρακτήρων για να αποκτήσουμε πρόσβαση σε αυτή. 9
Παράδειγμα 1 /* Μήκος συμβολοσειράς, εκδοχή με πίνακες */ int strlen (char s[]) { int n=0; while( s[n]!='\0') n++; return n; /* Νέα εκδοχή με δείκτες διεύθυνσης (pointers) */ int strlen (char *s) { int n; for (n=0; *s!='\0' ; n++) s++; return n; Στο αρχείο string.h υπάρχει ο ορισμός αυτής της συνάρτησης με πρωτότυπο: int strlen (char *s); 10
Παράδειγμα 2 /* Αντιγραφή του s στο d, εκδοχή με πίνακες */ void strcopy(char *d, char *s) { int i=0; while ((d[i]=s[i])!='\0') i++; return; /* Eκδοχή με δείκτες διεύθυνσης (pointers) */ void strcopy(char *d, char *s) { while ((*d=*s)!='\0') { d++; s++; return; /* ή ακόμα καλύτερα */ void strcopy(char *d, char *s) { while ((*d++=*s++)!='\0') ; return; Στο αρχείο string.h υπάρχει ο ορισμός της συνάρτησης με πρωτότυπο: char *strcpy(char *dest, const char *src); 11
Παράδειγμα 3 /* Επιστρέφει <0 αν s1<s2, 0 αν s1==s2, και >0 αν s1>s2 εκδοχή με πίνακες */ int strcomp(char *s1, char *s2) { int i; for (i=0; s1[i]==s2[i]; i++) if (s1[i]=='\0') return 0; return s1[i]-s2[i]; /* Εκδοχή με δείκτες διεύθυνσης (pointers) */ int strcomp(char *s1, char *s2) { for ( if (*s1=='\0') return 0; return *s1-*s2; ; *s1==*s2 ; s1++, s2++) Στο αρχείο string.h υπάρχει ο ορισμός της συνάρτησης με πρωτότυπο: int strcmp(const char *s1, const char *s2); 12
Κι άλλα παραδείγματα Συνάρτηση που να επιστρέφει τον ν-οστό όρο της ακολουθίας Fibonacci, όταν γνωρίζουμε ότι ο κάθε όρος προκύπτει από το άθροισμα των δύο προηγούμενων όρων και ότι ο πρώτος όρος ισούται με μηδέν (0), ενώ ο δεύτερος με ένα (1). Υλοποίηση ενός στοιχειώδους κατανεμητή μνήμης με συναρτήσεις για δέσμευση, alloc(n), και αποδέσμευση, afree(p), μνήμης. 13