epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε...

50
Φροντιστήρια Σύντομη Ιστορία της C Η γλώσσα προγραμματισμού C δημιουργήθηκε από τον Dennis Ritchie στα Bell Labs το 1972 όταν αυτός και ο Ken Thompson ασχολούνταν με τον σχεδιασμό του λειτουργικού συστήματος Unix. Η C ήταν μια εξέλιξη της γλώσσας Β του Ken Thompson και εκείνη με τη σειρά της ήταν μια εξέλιξη της γλώσσας BCPL και δημιουργήθηκε για να μπορέσει να καλύψει κάποιες αυξημένες ανάγκες στον προγραμματισμό. Αργότερα αναπτύχθηκαν πολλές παραλλαγές της C που ήταν επόμενο να έχουν ασυμφωνίες μεταξύ τους. Έτσι, δημιουργήθηκε μια επιτροπή στις αρχές του καλοκαιριού του 1983 που άρχισε να δουλεύει πάνω στη δημιουργία ενός προτύπου Ansi το οποίο και θα όριζε μια για πάντα τη γλώσσα C. Μια Σύγκριση των Γλωσσών Προγραμματισμού Η C θεωρείται γενικά γλώσσα μέσου επιπέδου και αυτό γιατί συνδυάζει στοιχεία των γλωσσών υψηλού επιπέδου (high level languages), όπως είναι η Cobol και η Pascal και στοιχεία των γλωσσών χαμηλού επιπέδου (low level languages), όπως είναι η Assembly. Για να διευκρινίσουμε, πρέπει να πούμε ότι σαν γλώσσα υψηλού επιπέδου θεωρείται η γλώσσα εκείνη που είναι αρκετά περιγραφική και που είναι έτσι πιο κοντά στην ανθρώπινη γραπτή γλώσσα και σαν γλώσσα χαμηλού επιπέδου θεωρείται εκείνη που είναι πιο κοντά στη μηχανή. Αυτός ο χαρακτηρισμός δεν έχει καμία απολύτως σχέση με τις δυνατότητες της γλώσσας. Σίγουρα, η κάθε γλώσσα προγραμματισμού έχει κάποιες προτεραιότητες να εκπληρώσει. Η Pascal, για παράδειγμα, χρησιμοποιείται κυρίως για τη σωστή διδασκαλία των αρχών του προγραμματισμού, ενώ η Basic δημιουργήθηκε έτσι ώστε να δώσει τη δυνατότητα σε αρχάριους στον προγραμματισμό να κάνουν με άνεση και ευκολία τα πρώτα τους βήματα στον ιδιόμορφο αυτό χώρο. Ο Clipper και η Cobol είναι καθαρά επαγγελματικές γλώσσες προγραμματισμού. Η C, όμως, μπόρεσε να φέρει τον προγραμματιστή πιο κοντά στο hardware, που με τις άλλες γνωστές γλώσσες προγραμματισμού κάτι τέτοιο θα ήταν πολύ δύσκολο να γίνει. Βέβαια, η κάθε γλώσσα προγραμματισμού κάνει και διαφορετική δουλειά και δεν θα ήταν σωστό να κάνουμε συγκρίσεις, για τον ίδιο λόγο που δεν μπορούμε να συγκρίνουμε ένα αεροπλάνο μένα ποδήλατο καθώς το καθένα είναι προορισμένο να κάνει διαφορετική δουλειά. Τα Πλεονεκτήματα της C Τα τελευταία χρόνια η C έχει καθιερωθεί ως μια από τις σημαντικότερες και δημοφιλέστερες γλώσσες προγραμματισμού. Τα σημαντικότερα πλεονεκτήματα που εξηγούν αυτήν την προτίμησή της, αναφέρονται παρακάτω. Χαρακτηριστικά Σχεδίασης, Η C έχει μοντέρνες δομές ελέγχου για να μπορούμε να κάνουμε επαναληπτικές εργασίες και για εύκολη επιλογή εναλλακτικών τρόπων δράσης. Με το πλήθος των δομών δεδομένων που διαθέτει, μπορεί να αναπαραστήσει ένα μεγάλο σύνολο από διαφορετικούς τύπους πληροφοριών. Έχει και το μεγάλο πλεονέκτημα ότι επιβάλλει τη διάσπαση του προγράμματος σε αυτοδύναμες ενότητες, τις συναρτήσεις. Αποτελεσματική, Η C είναι μια αποτελεσματική γλώσσα προγραμματισμού, που είναι τόσο συμπεριεκτική, ώστε να χρησιμοποιούμε σ' αυτήν πολύ λιγότερες λέξεις σε σχέση με άλλες γλώσσες. Έχει έναν συμπαγή και γρήγορο κώδικα. Φορητή Γλώσσα, Η C είναι μια φορητή γλώσσα, δηλ. τα προγράμματά της μπορούν να τρέξουν με λίγες ή και με καθόλου τροποποιήσεις και σε ένα άλλο σύστημα. Μάλιστα θεωρείται σαν η πιο φορητή γλώσσα. Δυναμικότητα και Ευελιξία, Η C είναι δυναμική και ευέλικτη, δύο ιδιότητες που είναι αρκετά δημοφιλείς στους υπολογιστές. Όπως ξέρουμε, το μεγαλύτερο μέρος του δυναμικού και ευέλικτου λειτουργικού συστήματος Unix είναι γραμμένο σε C. Αυτό ισχύει και για επεξεργαστές κειμένων, μεταγλωττιστές (compilers) και ερμηνευτές (interpreters) γλωσσών προγραμματισμού. Η C

Transcript of epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε...

Page 1: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Φροντιστήρια

Σύντομη Ιστορία της C Η γλώσσα προγραμματισμού C δημιουργήθηκε από τον Dennis Ritchie στα Bell Labs το 1972 όταν αυτός και ο Ken Thompson ασχολούνταν με τον σχεδιασμό του λειτουργικού συστήματος Unix. Η C ήταν μια εξέλιξη της γλώσσας Β του Ken Thompson και εκείνη με τη σειρά της ήταν μια εξέλιξη της γλώσσας BCPL και δημιουργήθηκε για να μπορέσει να καλύψει κάποιες αυξημένες ανάγκες στον προγραμματισμό. Αργότερα αναπτύχθηκαν πολλές παραλλαγές της C που ήταν επόμενο να έχουν ασυμφωνίες μεταξύ τους. Έτσι, δημιουργήθηκε μια επιτροπή στις αρχές του καλοκαιριού του 1983 που άρχισε να δουλεύει πάνω στη δημιουργία ενός προτύπου Ansi το οποίο και θα όριζε μια για πάντα τη γλώσσα C.

Μια Σύγκριση των Γλωσσών Προγραμματισμού Η C θεωρείται γενικά γλώσσα μέσου επιπέδου και αυτό γιατί συνδυάζει στοιχεία των γλωσσών υψηλού επιπέδου (high level languages), όπως είναι η Cobol και η Pascal και στοιχεία των γλωσσών χαμηλού επιπέδου (low level languages), όπως είναι η Assembly. Για να διευκρινίσουμε, πρέπει να πούμε ότι σαν γλώσσα υψηλού επιπέδου θεωρείται η γλώσσα εκείνη που είναι αρκετά περιγραφική και που είναι έτσι πιο κοντά στην ανθρώπινη γραπτή γλώσσα και σαν γλώσσα χαμηλού επιπέδου θεωρείται εκείνη που είναι πιο κοντά στη μηχανή. Αυτός ο χαρακτηρισμός δεν έχει καμία απολύτως σχέση με τις δυνατότητες της γλώσσας. Σίγουρα, η κάθε γλώσσα προγραμματισμού έχει κάποιες προτεραιότητες να εκπληρώσει. Η Pascal, για παράδειγμα, χρησιμοποιείται κυρίως για τη σωστή διδασκαλία των αρχών του προγραμματισμού, ενώ η Basic δημιουργήθηκε έτσι ώστε να δώσει τη δυνατότητα σε αρχάριους στον προγραμματισμό να κάνουν με άνεση και ευκολία τα πρώτα τους βήματα στον ιδιόμορφο αυτό χώρο. Ο Clipper και η Cobol είναι καθαρά επαγγελματικές γλώσσες προγραμματισμού. Η C, όμως, μπόρεσε να φέρει τον προγραμματιστή πιο κοντά στο hardware, που με τις άλλες γνωστές γλώσσες προγραμματισμού κάτι τέτοιο θα ήταν πολύ δύσκολο να γίνει. Βέβαια, η κάθε γλώσσα προγραμματισμού κάνει και διαφορετική δουλειά και δεν θα ήταν σωστό να κάνουμε συγκρίσεις, για τον ίδιο λόγο που δεν μπορούμε να συγκρίνουμε ένα αεροπλάνο μ’ ένα ποδήλατο καθώς το καθένα είναι προορισμένο να κάνει διαφορετική δουλειά.

Τα Πλεονεκτήματα της C Τα τελευταία χρόνια η C έχει καθιερωθεί ως μια από τις σημαντικότερες και δημοφιλέστερες γλώσσες προγραμματισμού. Τα σημαντικότερα πλεονεκτήματα που εξηγούν αυτήν την προτίμησή της, αναφέρονται παρακάτω. Χαρακτηριστικά Σχεδίασης, Η C έχει μοντέρνες δομές ελέγχου για να μπορούμε να κάνουμε

επαναληπτικές εργασίες και για εύκολη επιλογή εναλλακτικών τρόπων δράσης. Με το πλήθος των δομών δεδομένων που διαθέτει, μπορεί να αναπαραστήσει ένα μεγάλο σύνολο από διαφορετικούς τύπους πληροφοριών. Έχει και το μεγάλο πλεονέκτημα ότι επιβάλλει τη διάσπαση του προγράμματος σε αυτοδύναμες ενότητες, τις συναρτήσεις.

Αποτελεσματική, Η C είναι μια αποτελεσματική γλώσσα προγραμματισμού, που είναι τόσο συμπεριεκτική, ώστε να χρησιμοποιούμε σ' αυτήν πολύ λιγότερες λέξεις σε σχέση με άλλες γλώσσες. Έχει έναν συμπαγή και γρήγορο κώδικα.

Φορητή Γλώσσα, Η C είναι μια φορητή γλώσσα, δηλ. τα προγράμματά της μπορούν να τρέξουν με λίγες ή και με καθόλου τροποποιήσεις και σε ένα άλλο σύστημα. Μάλιστα θεωρείται σαν η πιο φορητή γλώσσα.

Δυναμικότητα και Ευελιξία, Η C είναι δυναμική και ευέλικτη, δύο ιδιότητες που είναι αρκετά δημοφιλείς στους υπολογιστές. Όπως ξέρουμε, το μεγαλύτερο μέρος του δυναμικού και ευέλικτου λειτουργικού συστήματος Unix είναι γραμμένο σε C. Αυτό ισχύει και για επεξεργαστές κειμένων, μεταγλωττιστές (compilers) και ερμηνευτές (interpreters) γλωσσών προγραμματισμού. Η C

Page 2: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

διαθέτει μερικά από τα χαρακτηριστικά ελέγχου που συνήθως τα συναντάμε στη συμβολική γλώσσα (assembly language).

Προσανατολισμός προς τον Προγραμματιστή, Η C είναι προσανατολισμένη προς τις ανάγκες του προγραμματιστή, ο οποίος και έχει άμεση πρόσβαση στο υλικό. Με τη C έχουμε τη σπουδαία δυνατότητα να μπορούμε να χειριζόμαστε μεμονωμένα τα δυαδικά ψηφία (bits) της μνήμης. Γενικά η C είναι πολύ λιγότερο περιοριστική στο να μας αφήνει να κάνουμε ό,τι θέλουμε σε σχέση με την Pascal για παράδειγμα.

Αυτή η ελευθερία είναι και πλεονέκτημα, αλλά είναι και επικίνδυνη όπως είναι φυσικό. Στη C τα πάντα (σχεδόν) επιτρέπονται. Δεν γίνεται έλεγχος των τύπων, άρα μπορεί κανείς να ανακατέψει ό,τι δεδομένα θέλει, κάτι που είναι πολύ χρήσιμο όταν προγραμματίζουμε σε επίπεδο συστήματος. Ακόμη, η C έχει μια τεράστια βιβλιοθήκη από χρήσιμες συναρτήσεις.

Τα Μειονεκτήματα της C Η C έχει και μειονεκτήματα, γιατί όπως πολύ καλά ξέρουμε η πολύ ελευθερία βλάπτει. Για παράδειγμα, η ελευθερία έκφρασης που αναφέραμε παραπάνω ότι έχει η C, απαιτεί από τον προγραμματιστή μια αυξημένη επαγρύπνηση και υπευθυνότητα. Ακόμη, η λακωνικότητα της C σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει σαν αποτέλεσμα τη δημιουργία προγραμμάτων που είναι τόσο δυσανάγνωστα, ώστε να είναι δύσκολο να τα κατανοήσει κάποιος με την πρώτη ματιά και πολλές φορές ακόμα και αυτός που τα έγραψε. Επιπλέον, συχνά είναι πολύ δύσκολο να ανιχνευθούν και τα λογικά λάθη σ’ ένα πρόγραμμα της C. Η C έχει τελικά τόσες πολλές δυνατότητες έκφρασης, ώστε να χρειαστεί πολύς καιρός για να μπορεί να πει κανείς με βεβαιότητα ότι την έμαθε καλά.

Γράψιμο προγράμματος σε C Όπως είπαμε στα προηγούμενα, η C επιβάλλει τον καταμερισμό του προγράμματος σε ενότητες, που ονομάζονται συναρτήσεις (functions). Εάν είναι απαραίτητο, οι συναρτήσεις μπορούν να χωριστούν και σε μικρότερες συναρτήσεις. Επίσης, στη C το κύριο πρόγραμμα είναι κι αυτό μια συνάρτηση, που ονομάζεται main(). Μια μέθοδος για το γράψιμο ενός προγράμματος στη C είναι να ξεκινήσουμε γράφοντας τη συνάρτηση main(), την ενότητα του πιο πάνω επιπέδου και μετά να ασχοληθούμε με τις συναρτήσεις των πιο κάτω επιπέδων. Η διαδικασία αυτή ονομάζεται πάνω-προς-τα-κάτω προγραμματισμός (top-down programming). Η αντίστροφη διαδικασία, δηλ. το να ασχοληθούμε πρώτα με τις συναρτήσεις των κατώτερων επιπέδων και μετά να ανεβαίνουμε προς τα πάνω, ονομάζεται κάτω-προς-τα-πάνω προγραμματισμός (bottom-up programming). Ένα πλεονέκτημα του πάνω-προς-τα-κάτω προγραμματισμού είναι ότι μπορούμε να χαράξουμε καλύτερα τη ροή του προγράμματος, μια και δεν ασχολούμαστε από την αρχή με τις λεπτομέρειες των επί μέρους συναρτήσεων.

Μεταγλώττιση και Σύνδεση Προγράμματος Ο μεταγλωττιστής (compiler) της C μετατρέπει τον πηγαίο κώδικα (source program), δηλ. το πρόγραμμα που γράφουμε σε C, σ’ έναν αντικειμενικό κώδικα (object program) και το πρόγραμμα σύνδεσης (linker) συνδυάζει αυτόν τον κώδικα με άλλους κώδικες και δημιουργείται έτσι το εκτελέσιμο αρχείο (executable file). Τα προγράμματα της C έχουν την επέκταση .c. Ο ρόλος του προγράμματος σύνδεσης είναι να ενώσει τον τελικό κώδικα, τον κώδικα εκκίνησης (start-up code) του συστήματός μας και τον κώδικα βιβλιοθήκης (library code) στο εκτελέσιμο αρχείο. Ο κώδικας εκκίνησης έχει σχέση με την επικοινωνία μεταξύ του προγράμματος και του λειτουργικού συστήματος και ο κώδικας βιβλιοθήκης περιέχει τον τελικό κώδικα για πολλές συναρτήσεις. Σε μερικά συστήματα πρέπει να τρέξουμε τα προγράμματα μεταγλώττισης και σύνδεσης ξεχωριστά, ενώ σ' άλλα ο μεταγλωττιστής ενεργοποιεί το πρόγραμμα σύνδεσης αυτόματα μόνος του.

Page 3: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Ένα Απλό Πρόγραμμα στη C /* prog01.c – αυτό είναι ένα απλό πρόγραμμα στη γλώσσα C */ #include <stdio.h> main() { int num; /* ορίζεται μια ακέραια μεταβλητή με το όνομα num */ num = 10 ; /* καταχώρηση τιμής στη μεταβλητή num */ printf("Ένα πολύ απλό πρόγραμμα σε C.\n"); /* η συνάρτηση printf() */ printf("Ο αγαπημένος μου αριθμός είναι ο %d.\n", num); } Αφού μεταγλωττίσουμε και τρέξετε το παραπάνω πρόγραμμα, τότε θα πρέπει να εμφανισθούν στην οθόνη τα παρακάτω : Ένα πολύ απλό πρόγραμμα σε C. Ο αγαπημένος μου αριθμός είναι ο 10.

Σύντομη Ανάλυση του Προγράμματος Η πρώτη γραμμή του προγράμματος χρησιμοποιεί τα σύμβολα /* και */ για να συμπεριλάβουμε εκεί κάποια σχόλια (comments), που θα μας βοηθήσουν να κάνουμε το πρόγραμμά μας πιο ευανάγνωστο. Αυτά τα σχόλια αγνοούνται από τον υπολογιστή. Η δεύτερη γραμμή λέει στον υπολογιστή να συμπεριλάβει (include) τις πληροφορίες που υπάρχουν στο αρχείο stdio.h, το οποίο αποτελεί μέρος του πακέτου της γλώσσας C. Τα προγράμματα της C αποτελούνται από μία ή περισσότερες συναρτήσεις, που είναι και οι βασικές ενότητες ενός προγράμματος C. Αυτό το πρόγραμμα αποτελείται από μία μόνο συνάρτηση που καλείται main(). Οι παρενθέσεις υποδηλώνουν ότι το main() είναι ένα όνομα μιας συνάρτησης. Η αγκύλη { δηλώνει την αρχή των προτάσεων που αποτελούν τη συνάρτηση και ο ορισμός της συνάρτησης τελειώνει με την αντίστοιχη αγκύλη }. Οι αγκύλες { και } είναι αντίστοιχες με τα begin και end της Pascal. Η πρόταση δήλωσης μάς λέει ότι θα χρησιμοποιήσουμε μια μεταβλητή με το όνομα num και ότι η num είναι ακέραια μεταβλητή (integer). Η πρόταση καταχώρησης δίνει την τιμή 10 στη μεταβλητή num. Η επόμενη γραμμή είναι για την εκτύπωση της φράσης που βρίσκεται μεταξύ των εισαγωγικών (" "). Το \n λέει στον υπολογιστή να ξεκινήσει μια νέα γραμμή (σαν να πατούσαμε το πλήκτρο <enter>). Η επόμενη γραμμή είναι για την εκτύπωση της τιμής της num (που είναι το 10) ανάμεσα στη φράση που βρίσκεται μεταξύ των " ". Η εντολή %d λέει στον υπολογιστή πού και με ποια μορφή να εκτυπώσει την τιμή της num. Το πρόγραμμα τελειώνει με την αγκύλη }.

Λεπτομερής Ανάλυση του Προγράμματος Τα Αρχεία Επικεφαλίδας #include Το αρχείο stdio.h αποτελεί μέρος του μεταγλωττιστή της C και περιέχει πληροφορίες σχετικές με συναρτήσεις εισόδου και εξόδου, όπως είναι η printf(), που χρησιμοποιεί ο μεταγλωττιστής. Το όνομά του προέρχεται από τις λέξεις standard input/output header. Οι χρήστες της C αναφέρουν σαν επικεφαλίδα μια συλλογή πληροφοριών που βρίσκεται στην αρχή ενός αρχείου. Το αποτέλεσμα της εντολής #include <stdio.h> θα ήταν το ίδιο με το να αντιγράφαμε όλο το περιεχόμενο του αρχείου stdio.h στο δικό μας αρχείο, στη θέση όπου εμφανίζεται αυτή η γραμμή προγράμματος. Στην πραγματικότητα, αυτή η γραμμή προγράμματος δεν είναι καν μια πρόταση της γλώσσας C. Το σύμβολο # σημαίνει ότι τη γραμμή αυτή τη διαχειρίζεται ο προεπεξεργαστής (preprocessor) της C, ο οποίος διαχειρίζεται κάποιες εργασίες πριν από τον μεταγλωττιστή. Η Συνάρτηση main() Η εκτέλεση ενός προγράμματος σε C αρχίζει πάντα με μια συνάρτηση που αποκαλείται main(). Είμαστε ελεύθεροι να επιλέξουμε τα ονόματα των άλλων συναρτήσεων που ίσως χρησιμοποιήσουμε, αλλά θα πρέπει υποχρεωτικά να υπάρχει η συνάρτηση main() στην αρχή του προγράμματος. Οι

Page 4: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

παρενθέσεις δηλώνουν ότι η main() είναι μια συνάρτηση. Γενικά, οι παρενθέσεις περιέχουν πληροφορίες (ορίσματα) που θα περάσουν μέσα στη συνάρτηση. Όταν, βέβαια, δεν υπάρχουν πληροφορίες για να περάσουν (μεταβιβασθούν), τότε οι παρενθέσεις είναι άδειες. Τα Σχόλια Όταν στο πρόγραμμά μας έχουμε σχόλια, τότε είναι πολύ ευκολότερο σε κάποιον άλλον, αλλά ακόμα και σε μας, να καταλάβει τι κάνει το πρόγραμμά μας. Ο,τιδήποτε υπάρχει ανάμεσα στο σύμβολο ανοίγματος /* και στο σύμβολο κλεισίματος */ των σχολίων αγνοείται από τον υπολογιστή και τα σχόλια στη C μπορούν να τοποθετηθούν οπουδήποτε, ακόμα και σε πολλές συνεχόμενες γραμμές. Αγκύλες, Σώματα, Μπλοκ Οι αγκύλες { και } δηλώνουν την αρχή και το τέλος του σώματος μιας συνάρτησης και μπορούν ακόμη να χρησιμοποιηθούν για να συμπεριλάβουν μαζί προτάσεις μέσα σε μια ομάδα ή σ’ ένα μπλοκ του προγράμματος, κάτι δηλαδή παρόμοιο με τα begin και end της Pascal. Οι Προτάσεις Δήλωσης Η πρόταση δήλωσης είναι μια από τις σημαντικότερες προτάσεις της C. Η δήλωση αυτή σημαίνει ότι κάπου μέσα στη συνάρτηση χρησιμοποιούμε τη μεταβλητή που δηλώνουμε και ότι ο τύπος της είναι αυτός που δείχνουμε, π.χ. ακέραιος. Η λέξη int είναι μια λέξη-κλειδί της C, δηλ. δεν μπορεί να χρησιμοποιηθεί αλλού μέσα στο πρόγραμμα σαν όνομα μιας συνάρτησης ή μιας μεταβλητής. Το ερωτηματικό στο τέλος της γραμμής δηλώνει ότι η γραμμή αυτή αποτελεί μια πρόταση ή εντολή της C.

Οι Τύποι Δεδομένων και οι Μεταβλητές Η C έχει διάφορα είδη (τύπους) δεδομένων : ακέραιους, χαρακτήρες, κινητής υποδιαστολής, αριθμούς κ.ά. Για το όνομα μιας μεταβλητής μπορούμε να χρησιμοποιήσουμε μικρά γράμματα, κεφαλαία γράμματα, ψηφία αριθμών και τον χαρακτήρα της υπογράμμισης (_). Ο πρώτος χαρακτήρας, όμως, πρέπει να είναι πάντα γράμμα. Πρέπει να είναι όλες οι μεταβλητές του προγράμματος συγκεντρωμένες μαζί για να είναι ευκολότερο για τον αναγνώστη να καταλάβει τι κάνει το πρόγραμμα και ακόμη πρέπει να υπάρχουν και σχόλια δίπλα στην κάθε μεταβλητή που να εξηγούν την αποστολή της. Ακόμη, η ονομασία που δίνουμε στις μεταβλητές πρέπει να μας βοηθάει να καταλαβαίνουμε και τη χρήση τους. Η Εντολή Καταχώρησης Η πρόταση καταχώρησης είναι μια από τις βασικότερες προτάσεις της C και με τη χρήση της δίνουμε τιμές στις μεταβλητές του προγράμματος. Η πρόταση καταχώρησης ολοκληρώνεται με το Ελληνικό ερωτηματικό (;) . Η Συνάρτηση printf() Οι παρενθέσεις δηλώνουν κατ' αρχήν ότι πρόκειται για συνάρτηση. Τα στοιχεία που περιέχονται μεταξύ των παρενθέσεων είναι οι πληροφορίες που περνάμε (μεταβιβάζουμε) από τη συνάρτηση main() στη συνάρτηση printf(). Η πληροφορία που περνάει λέγεται όρισμα της συνάρτησης και η συγκεκριμένη συνάρτηση printf() εξετάζει τι υπάρχει μεταξύ των " " και το απεικονίζει στην οθόνη ενός τερματικού. Για να καλέσουμε μια συνάρτηση το μόνο που χρειάζεται να κάνουμε είναι να γράψουμε το όνομά της και να συμπεριλάβουμε το επιθυμητό όρισμα μέσα σε παρενθέσεις. Όταν το πρόγραμμα φθάσει σ’ αυτή τη γραμμή, τότε ο έλεγχος περνάει στη συνάρτηση και μετά την ενεργοποίησή της, ο έλεγχος επιστρέφει στο αρχικό πρόγραμμα. Ο χαρακτήρας \n, που δεν εμφανίζεται όταν τρέξει το πρόγραμμα, είναι στην πραγματικότητα μια εντολή για το ξεκίνημα μιας νέας γραμμής. Ο συνδυασμός \n αποτελεί έναν απλό χαρακτήρα που καλείται χαρακτήρας νέας γραμμής, δηλ. ενεργεί όπως το πλήκτρο <enter>. Οχαρακτήρας νέας γραμμής είναι ένα παράδειγμα αυτού που ονομάζεται ακολουθία διαφυγής και που χρησιμοποιείται για να παριστάνει δύσκολους ή αδύνατον να πληκτρολογηθούν χαρακτήρες. Άλλα παραδείγματα τέτοιων χαρακτήρων είναι το \t για το πλήκτρο tab και το\b για ένα διάστημα προς τα πίσω (backspace). Τι είναι, όμως, το σύμβολο %d;

Page 5: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Δείχνει σε ποιο σημείο θα εμφανιστεί η τιμής μιας μεταβλητής και ότι η μεταβλητή αυτή είναι ένας ακέραιος αριθμός. Μια συνάρτηση στη C αποτελείται από μια επικεφαλίδα και ένα σώμα. Η επικεφαλίδα περιέχει τις προτάσεις του προεπεξεργαστή, όπως είναι η #include, και το όνομα της συνάρτησης. Το σώμα της συνάρτησης βρίσκεται μέσα στις αγκύλες { και } και αποτελείται από μια σειρά προτάσεων, που η κάθε μια τελειώνει μ’ ένα ελληνικό ερωτηματικό ;. Μέσα στο σώμα της συνάρτησης μπορεί να υπάρχουν προτάσεις δήλωσης, καταχώρησης και κλήσης άλλων συναρτήσεων.

Τεχνικές για πιο Ευανάγνωστα Προγράμματα Δύο τέτοιες τεχνικές που είδαμε στα προηγούμενα ήταν η χρήση σχολίων και η σωστή ονομασία των μεταβλητών. Μια άλλη τεχνική είναι η χρήση κενών γραμμών για να ξεχωρίζουν τα τμήματα του προγράμματος. Ακόμη, στη C μπορούμε να τοποθετήσουμε πολλές προτάσεις σε μια γραμμή ή και να χωρίσουμε μια πρόταση σε πολλές γραμμές. Το ελληνικό ερωτηματικό λέει στον μεταγλωττιστή πού τελειώνει μια πρόταση και πού αρχίζει η επόμενη. Πιο σωστό είναι πάντως να γράφουμε μια πρόταση ανά γραμμή.

Ένα Ακόμη Παράδειγμα στη C /* prog02.c – το πρόγραμμα αυτό μετατρέπει τα μέτρα σε εκατοστά */ #include <stdio.h> main() { int cm, metres; metres = 2; cm = 100 * metres; printf("Υπάρχουν %d εκατοστά σε %d μέτρα. \n", cm, metres); } Όπως βλέπουμε, το πρώτο σχόλιο του προγράμματος περιέχει το όνομά του και το τι ακριβώς κάνει. Ακόμη, το πρόγραμμα δηλώνει δύο ακέραιες μεταβλητές μαζί, τις οποίες και διαχωρίζει με κόμμα. Χρησιμοποιεί τον τελεστή του πολλαπλασιασμού, που είναι το * και εκτυπώνει πολλές μεταβλητές μαζί στη συνάρτηση printf(). Όταν τρέξει το πρόγραμμα, θα δώσει το εξής αποτέλεσμα : Υπάρχουν 200 εκατοστά σε 2 μέτρα.

Κλήση Συνάρτησης Στο επόμενο παράδειγμα θα δούμε πώς μπορούμε να συμπεριλάβουμε και να καλούμε και μια δική μας συνάρτηση. /* prog03.c – ένα πρόγραμμα που καλεί μια συνάρτηση */ #include <stdio.h> main() { printf("Θα καλέσω τη συνάρτηση test.\n"); test(); printf("Η κλήση της συνάρτησης έγινε.\n"); } patra() { printf("Γεια σας από την test.\n"); } Το αποτέλεσμα του προγράμματος θα είναι το παρακάτω : Θα καλέσω τη συνάρτηση της test.

Page 6: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Γεια σας από τη test. Η κλήση της συνάρτησης έγινε. Η συνάρτηση test() ορίζεται με τον ίδιο τρόπο, όπως και η main(), με το σώμα της να βρίσκεται ανάμεσα σε αγκύλες. Η συνάρτηση καλείται δίνοντας απλά το όνομά της μαζί με τις παρενθέσεις. Όταν η συνάρτηση test() τελειώσει τη δουλειά της, το πρόγραμμα προχωράει στην επόμενη πρόταση της main(). Πρέπει να έχουμε υπόψη μας ότι οι συναρτήσεις μπορούν να γραφούν είτε πριν ή μετά από την κύρια συνάρτηση main(), αλλά εκτελούνται μόνο όταν και όπου η main() τις καλεί.

Έλεγχος της Ορθότητας των Προγραμμάτων

Συντακτικά Λάθη

Το συντακτικό λάθος στη C είναι κάτι ανάλογο με το γραμματικό λάθος στη γλώσσα που μιλάμε. Συντακτικά λάθη στη C μπορούν να γίνουν και με τη χρήση επιτρεπτών συμβόλων σε λανθασμένες θέσεις. Παραδείγματα τέτοιων λαθών μπορεί να είναι η μη σωστή χρήση των αγκυλών { και } ή ακόμη το να ανοίγουμε μια αγκύλη και να μην την κλείνουμε, το να ανοίγουμε κάπου σχόλια και να ξεχνάμε να τα κλείσουμε κοκ. Όπως ξέρουμε, μέρος της δουλειάς του μεταγλωττιστή είναι και η ανακάλυψη των συντακτικών λαθών του προγράμματος. Υπάρχουν, όμως, και περιπτώσεις όπου ένα λάθος παράγει, άθελά μας, και άλλα λάθη.

Εννοιολογικά Λάθη

Το εννοιολογικό λάθος είναι το λάθος στο νόημα των προτάσεων. Στη C εννοιολογικά λάθη μπορούμε να κάνουμε, όταν ακολουθούμε μεν σωστά τους κανόνες της γλώσσας, αλλά με λανθασμένο αποτέλεσμα. Τέτοιο λάθος μπορεί να γίνει, όταν π.χ. αντί να προσθέσουμε δύο μεταβλητές, τις πολλαπλασιάζουμε. Με τα λάθη αυτά βέβαια δεν έχει καμία σχέση ο μεταγλωττιστής. Είναι δική μας δουλειά να τα ανακαλύψουμε και να τα διορθώσουμε. Ο καλύτερος τρόπος για να ανακαλύψουμε τέτοια λάθη είναι να εξετάσουμε το πρόγραμμα βήμα-βήμα. Μπορούμε ακόμα να χρησιμοποιούμε επιλεκτικά και τη συνάρτηση printf() μέσα στο πρόγραμμα, ώστε να ελέγχουμε τις τιμές κάποιων μεταβλητών του προγράμματος. Τις εντολές printf() τις απομακρύνουμε μετά όταν το πρόγραμμά μας λειτουργήσει κανονικά. Και η χρήση των σχολίων μπορεί να αποδειχθεί χρήσιμη εδώ, γιατί με τη βοήθειά τους μπορούμε να απομονώσουμε κάποιο κομμάτι του προγράμματος προσωρινά και να ελέγξουμε έτσι την ορθότητα του υπόλοιπου προγράμματος. Υπάρχουν και ειδικά προγράμματα που λέγονται αποσφαλματωτές (debuggers) και που μας επιτρέπουν να βλέπουμε τις τιμές των μεταβλητών του προγράμματος και ποια γραμμή του προγράμματος εκτελείται.

Οι Λέξεις-Κλειδιά της ANSI C Οι λέξεις-κλειδιά ή δεσμευμένες λέξεις (reserved words) αποτελούν το λεξιλόγιο της C και γι’ αυτόν τον λόγο δεν μπορούμε να τις χρησιμοποιούμε για να δηλώσουμε ονόματα μεταβλητών ή συναρτήσεων στα προγράμματά μας. Οι λέξεις αυτές είναι οι εξής :

auto break case char const

continue default do double else

enum extern float for goto

if int long register return

short signed sizeof static struct

switch typedef union unsigned void

Page 7: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

volatile while

Θα δούμε τώρα τα δεδομένα και τις ιδιότητές τους και θα μελετήσουμε κυρίως τις τρεις μεγάλες οικογένειες, των ακεραίων αριθμών, των αριθμών κινητής υποδιαστολής και των χαρακτήρων.

Ένα Απλό Πρόγραμμα Ακολουθεί ένα απλό πρόγραμμα σε C. /* prog04.c – η αξία ενός ποσού σε ευρώ */ #include <stdio.h> main() { float draxmes, euro; /* 2 μεταβλητές κινητής υποδιαστολής */ char beep; /* μια μεταβλητή τύπου χαρακτήρα */ beep = ‘\007’ /* καταχώρηση ενός ειδικού χαρακτήρα */ printf("Θέλετε να μάθετε την αξία των χρημάτων σας σε δραχμές;\n"); printf("Παρακαλώ δώστε την αξία σε ευρώ \n"); printf("και θα δούμε.\n"); scanf("%f", &euro); /* παίρνουμε δεδομένα από τον χρήστη */ draxmes = 340.75 * euro; /* ο συντελεστής 340.75 μετατρέπει τις δραχμές σε ευρώ */ printf("%c Η αξία σε δραχμές είναι %.2f%c. \n", beep, draxmes, beep); } Αφού τρέξουμε αυτό το πρόγραμμα, θα πάρουμε το εξής αποτέλεσμα : Θέλετε να μάθετε την αξία των χρημάτων σας σε δραχμές; Παρακαλώ δώστε την αξία σε ευρώ και θα δούμε. 100.00 Η αξία σε δραχμές είναι 34075.00

Τι Καινούργια Στοιχεία Υπάρχουν Πρώτα απ' όλα χρησιμοποιούμε δύο νέα είδη μεταβλητών, μια μεταβλητή κινητής υποδιαστολής (float) και μια μεταβλητή χαρακτήρων (char) και συνεπώς μπορούμε τώρα να διαχειριστούμε περισσότερα δεδομένα. Για την εμφάνιση στην οθόνη αυτών των νέων ειδών μεταβλητών χρησιμοποιούμε τους κώδικες %f και %c της printf() αντίστοιχα. Η χρήση του .2 σημαίνει δύο δεκαδικά ψηφία στην εμφάνιση του δεκαδικού αριθμού, άσχετα αν με τις δραχμές δεν είχαμε δεκαδικά ψηφία. Για την εισαγωγή δεδομένων από το πληκτρολόγιο χρησιμοποιούμε τη συνάρτηση scanf(). Ο κώδικας %f καθοδηγεί το πρόγραμμα να διαβάσει έναν αριθμό κινητής υποδιαστολής και το &euro λέει στη συνάρτηση scanf() να καταχωρήσει την τιμή στη μεταβλητή euro. Το σύμβολο & θα αναλυθεί αργότερα. Οι δύο συναρτήσεις printf() και scanf() επιτυγχάνουν τη διαλογική επικοινωνία μας με τον υπολογιστή, αφού η μεν scanf() διαβάζει δεδομένα από το πληκτρολόγιο και τα μεταφέρει για επεξεργασία στο πρόγραμμα και η printf() διαβάζει δεδομένα από το πρόγραμμα και τα εμφανίζει στην οθόνη. Υπάρχει ακόμα και ο χαρακτήρας beep που παριστάνει το ηχητικό σήμα.

Οι Τύποι Δεδομένων Ο τύπος μιας μεταβλητής μπορεί να καθοριστεί σε μια πρόταση δήλωσης. Η C χρησιμοποιεί τις εξής επτά λέξεις-κλειδιά για τους τύπους δεδομένων της : int, long, short, unsigned, char, float και double. Η int δηλώνει τον βασικό τύπο ακεραίων και οι long, short και unsigned χρησιμοποιούνται σε παραλλαγές του βασικού τύπου. Η char χρησιμοποιείται γενικά για τους χαρακτήρες και οι float,

Page 8: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

double και ο συνδυασμός long double χρησιμοποιούνται για την παράσταση αριθμών κινητής υποδιαστολής. Οι πέντε πρώτες λέξεις-κλειδιά δημιουργούν ακέραιους τύπους, ενώ οι δύο τελευταίες τύπους κινητής υποδιαστολής. Οι ακέραιοι αποθηκεύονται σε μορφή ψηφιακών αριθμών, ενώ για τους αριθμούς κινητής υποδιαστολής υπάρχουν πολλοί τρόποι για να τους παραστήσουμε μέσα στον υπολογιστή. Ο πιο γνωστός είναι με τη χρήση του συμβόλου Ε, όπου και έχουμε το κλασματικό και το εκθετικό τμήμα του αριθμού, τα οποία και αποθηκεύονται ξεχωριστά. Έτσι, ο 7 γράφεται σαν 0.7Ε1, που σημαίνει 0.7 Χ 101 = 7. Κάτι που πρέπει να τονιστεί για τους αριθμούς κινητής υποδιαστολής είναι τα συχνά σφάλματα στρογγυλοποίησης που μπορεί να συμβούν μ' αυτούς και έτσι σαν γενικό κανόνα πρέπει να έχουμε υπόψη μας το ότι όταν οι ακέραιοι αριθμοί κάνουν τη δουλειά μας, να τους χρησιμοποιούμε ανεπιφύλακτα.

Οι Ακέραιοι Τύποι Δεδομένων Ο τύπος int που ήδη ξέρουμε είναι ένας ακέραιος με πρόσημο, δηλ. πρέπει να είναι ένας ολόκληρος ακέραιος και μπορεί να είναι θετικός, αρνητικός ή και μηδέν. Συνήθως χρησιμοποιούνται δύο bytes για να αποθηκευθεί ένας ακέραιος της μορφής αυτής και έτσι το εύρος τιμών του είναι από -32768 έως και +32767. Η λέξη-κλειδί int χρησιμοποιείται για τη δήλωση μεταβλητών αυτού του τύπου. Για να δηλώσουμε περισσότερες μεταβλητές αυτού του τύπου, μπορούμε είτε να τις δηλώσουμε χωριστά, ή να τις δηλώσουμε όλες μαζί, αλλά χωρισμένες με κόμμα. Οι δηλώσεις δημιουργούν μεταβλητές, αλλά δεν καταχωρούν τιμές σ' αυτές. Η καταχώρηση των τιμών γίνεται μέσα στο πρόγραμμα με τις εντολές καταχώρησης ή και μέσω της συνάρτησης scanf(). Απόδοση αρχικής τιμής σε μια μεταβλητή σημαίνει να ορίσουμε την τιμή εκκίνησης της μεταβλητής. Στη C αυτό μπορεί να γίνει σε μια πρόταση δήλωσης, όπου μετά το όνομα της μεταβλητής υπάρχει ο τελεστής καταχώρησης (=) και μετά η αρχική τιμή της μεταβλητής, ως εξής : int a=10; int b, cs=20; Στη C ειδικά προθέματα δηλώνουν ποια βάση αρίθμησης χρησιμοποιούμε. Το πρόθεμα 0 (μηδέν) σημαίνει ότι γράφουμε στο οκταδικό και το πρόθεμα 0x ή 0X σημαίνει ότι ο αριθμός είναι δεκαεξαδικός. Έτσι, ο δεκαδικός αριθμός 16 γράφεται στο οκταδικό σύστημα σαν 020 και στο δεκαεξαδικό σύστημα σαν 0x10 ή 0X10. Σ' όλες τις περιπτώσεις χρησιμοποιείται ο δυαδικός κώδικας από τον υπολογιστή για την αποθήκευση του αριθμού. Το σύμβολο %d που είδαμε στα προηγούμενα παραδείγματα αντιστοιχεί σε μια ακέραια τιμή αριθμού, που μπορεί να είναι μια μεταβλητή τύπου int, μια ακέραια σταθερά τύπου int ή και οποιαδήποτε άλλη έκφραση, που έχει όμως μια ακέραια τιμή int. Ακολουθεί ένα απλό πρόγραμμα που επεξηγεί τα παραπάνω : /* prog05.c – μερικές ιδιότητες της printf() */ #include <stdio.h> main() { int ten = 10; printf("%d μείον %d ίσον %d\n", ten, 2, ten-2); } Το αποτέλεσμα του προγράμματος θα είναι : 10 μείον 2 ίσον 8 Μπορούμε, όμως, να εκτυπώσουμε τους ακέραιους αριθμούς στο οκταδικό σύστημα με το σύμβολο %o και στο δεκαεξαδικό με το σύμβολο %x. Ακολουθεί ένα απλό παράδειγμα : /* prog06.c – εκτύπωση του αριθμού 100 σε τρία αριθμητικά συστήματα */ #include <stdio.h>

Page 9: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

main() { int x = 100; printf("δεκαδ = %d; οκταδ = %o; δεκαεξ = %x\n", x, x, x); } Το αποτέλεσμα του προγράμματος θα είναι : δεκαδ = 100; οκταδ = 144; δεκαεξ = 64 Παρατηρούμε ότι τα προθέματα 0 και 0x δεν παρουσιάζονται στην έξοδο.

Άλλοι Τύποι Ακεραίων Η C προσφέρει τρεις τύπους λέξεις-κλειδιά για την τροποποίηση του βασικού τύπου ακεραίου : unsigned, long και short. Ο τύπος short int ή short είναι ένας τύπος με πρόσημο και χρησιμοποιεί λιγότερο χώρο αποθήκευσης, απ' ό,τι ο int και έτσι μπορεί να χρησιμοποιηθεί για μικρούς αριθμούς. Ο τύπος long int ή long είναι ένας τύπος με πρόσημο που χρησιμοποιεί περισσότερο χώρο αποθήκευσης απ' ό,τι ο int και συνεπώς επιτρέπει τη χρήση μεγάλων ακεραίων. Ο τύπος unsigned επιτρέπει τη χρήση της κλίμακας από το 0 μέχρι το 65535 αντί για την κλίμακα από το -32768 μέχρι το +32767. Όσον αφορά τώρα τη χρήση αυτών των τύπων ακεραίων, πρέπει να έχουμε υπόψη μας ότι ο τύπος unsigned μπορεί να χρησιμοποιηθεί για μέτρημα, αφού δεν υπάρχουν σ' αυτόν τον τύπο αρνητικοί αριθμοί. Ο τύπος long χρησιμοποιείται αν έχουμε πολύ μεγάλους αριθμούς, που δεν μπορεί να τους διαχειριστεί ο τύπος int. Δεν πρέπει, όμως, να χρησιμοποιείται ο τύπος long αν αυτό δεν είναι απαραίτητο, γιατί επιβραδύνει τους υπολογισμούς και καταλαμβάνει πολύ μνήμη. Μπορούμε να χρησιμοποιήσουμε τον τύπο short όταν χρησιμοποιούμε μεγάλους πίνακες ακεραίων στο πρόγραμμά μας. Για να αποθηκευθεί μια σταθερά σαν τύπου long, θα πρέπει να προσθέσουμε την κατάληξη l ή L και οι καταλήξεις αυτές μπορούν να χρησιμοποιηθούν ακόμα και με τους οκταδικούς ή τους δεκαεξαδικούς ακέραιους. Για την εκτύπωση ενός αριθμού τύπου unsigned χρησιμοποιούμε το σύμβολο %u, ενός αριθμού τύπου long το %ld και ακόμη μπορούμε να συνδυάσουμε το l με τα o και x. Ακόμη, το %h χρησιμοποιείται για τον τύπο short.

Οι Χαρακτήρες Όπως ήδη ξέρουμε, ο τύπος char χρησιμοποιείται για την αποθήκευση χαρακτήρων, αν και στην πραγματικότητα αποθηκεύει ακέραιους. Για τον χειρισμό των χαρακτήρων ο υπολογιστής χρησιμοποιεί έναν αριθμητικό κώδικα, όπου συγκεκριμένοι ακέραιοι παριστάνουν συγκεκριμένους χαρακτήρες. Ο πιο συχνά χρησιμοποιούμενος κώδικας είναι ο ASCII και για παράδειγμα ο ακέραιος 65 παριστάνει το γράμμα Α. Οι μεταβλητές τύπου char δηλώνονται όπως όλες οι μεταβλητές : char name, city; Υπάρχουν εκδόσεις της C που έχουν τον τύπο char με πρόσημο και με περιοχή τιμών από -128 έως +127 και άλλες εκδόσεις της C έχουν τον τύπο char χωρίς πρόσημο και με περιοχή τιμών από 0 έως 255. Μπορούμε να δώσουμε αρχικές τιμές στις μεταβλητές τύπου char με δύο τρόπους : char grade = 65; /* εδώ χρησιμοποιούμε τον ASCII κώδικα του Α */ ή char grade = 'A'; /* εδώ τα πράγματα είναι πιο απλά για μας */ Τα απλά εισαγωγικά ' ' δηλώνουν στη C μια σταθερά χαρακτήρα, ενώ τα διπλά εισαγωγικά " " θεωρούνται μια συμβολοσειρά, που θα την δούμε αναλυτικά παρακάτω.

Οι μη Εκτυπούμενοι Χαρακτήρες Υπάρχουν μερικοί ASCII χαρακτήρες που είναι μη εκτυπούμενοι, όπως το διάστημα προς τα πίσω (backspace), το enter και το ηχητικό σήμα (beep). Ενεργοποιούνται με μια ειδική ακολουθία συμβόλων, που ονομάζεται ακολουθία διαφυγής (escape sequence) : \a ειδοποίηση - beep (ANSI C)

Page 10: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

\b ένα διάστημα προς τα πίσω (προσοχή δεν σβήνει τους χαρακτήρες) \f τροφοδότηση σελίδας \n νέα γραμμή (enter) \r επιστροφή στην αρχή της τρέχουσας γραμμής \t οριζόντια στηλοθέτηση (tab) \v κάθετη στηλοθέτηση (ANSI C) \\ πλάγια κάθετος (\) \' απλά εισαγωγικά (') \" διπλά εισαγωγικά (") (ANSI C) Όλοι αυτοί οι χαρακτήρες κλείνονται σε απλά εισαγωγικά όταν καταχωρούνται σε μια μεταβλητή χαρακτήρα : next = '\n'; Η εκτύπωση της μεταβλητής next προχωράει την εκτύπωση κατά μια γραμμή στην οθόνη ή στον εκτυπωτή. Η συνάρτηση printf() χρησιμοποιεί το %c για να δείξει ότι πρέπει να τυπωθεί ένας χαρακτήρας. Θα πρέπει να θυμόμαστε ότι οι χαρακτήρες είναι αποθηκευμένοι σαν ακέραιοι, οπότε αν τυπώσουμε την τιμή μιας μεταβλητής τύπου char, θα πάρουμε έναν ακέραιο. Ο προσδιορισμός %c λέει στην printf() να μετατρέψει τον ακέραιο στον αντίστοιχο χαρακτήρα. Αυτά φαίνονται καθαρά στο παρακάτω πρόγραμμα : /* prog07.c - εμφανίζει τον κωδικό αριθμό κάποιου χαρακτήρα */ #include <stdio.h> main() { char ch; printf("Δώστε έναν χαρακτήρα : \n"); scanf("%c", &ch); printf("Ο κώδικας του %c είναι το %d. \n", ch, ch); } Το αποτέλεσμα θα είναι : Δώστε έναν χαρακτήρα : C Ο κώδικας του C είναι το 67. Η printf() τυπώνει την τιμή της ch δύο φορές, την πρώτη σαν χαρακτήρα (προτρεπόμενη από τον κώδικα %c) και τη δεύτερη σαν ακέραιο (προτρεπόμενη από τον κώδικα %d). Οι προσδιοριστές της printf() αποφασίζουν για το πώς θα εμφανιστούν τα δεδομένα και όχι για το πώς θα αποθηκευθούν.

Οι Τύποι float και double Σε προγράμματα μαθηματικής φύσης συχνά χρησιμοποιούμε αριθμούς κινητής υποδιαστολής που στη C λέγονται τύπου float και είναι αντίστοιχοι με τους τύπους real της Pascal. Έτσι, μπορούμε να παραστήσουμε ένα πολύ μεγαλύτερο εύρος αριθμών, μεγάλων και μικρών. Υπάρχουν ακόμα και οι τύποι double (για διπλή ακρίβεια) και long double. Οι μεταβλητές αυτές δηλώνονται όπως και οι άλλες : float a=10.25e-3; double b; long double c; Ένα πρόθεμα f ή F στην ANSI C σ’ έναν αριθμό κινητής υποδιαστολής τον κάνει τύπου float, π.χ. 2.3f και 9.11E9F, ενώ μια κατάληξη L τον κάνει τύπου long double, π.χ. 54.31l και 4.32e4L. Η συνάρτηση printf() χρησιμοποιεί τον προσδιοριστή μορφής %f για να τυπώσει τύπου float και double αριθμούς με δεκαδικό συμβολισμό και τη μορφή %e για να τους τυπώσει σε εκθετικό συμβολισμό. Για τους τύπους long double έχουμε αντίστοιχα τους προσδιοριστές %Lf και %Le.

Page 11: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Ο Τελεστής sizeof Η C έχει έναν εσωτερικό τελεστή, τον sizeof, που δίνει το μέγεθος κάποιων πραγμάτων σε bytes. Αυτό θα το δούμε καλύτερα μ’ ένα παράδειγμα : /* prog08.c – τυπώνει τα μεγέθη των τύπων δεδομένων */ #include <stdio.h> main() { printf("Ο τύπος int έχει μέγεθος %d bytes. \n", sizeof(int)); printf("Ο τύπος char έχει μέγεθος %d bytes. \n", sizeof(char)); printf("Ο τύπος long έχει μέγεθος %d bytes. \n", sizeof(long)); printf("Ο τύπος double έχει μέγεθος %d bytes. \n", sizeof(double)); } Το αποτέλεσμα, αναλόγως με το σύστημα θα είναι κάπως έτσι: Ο τύπος int έχει μέγεθος 2 bytes. (Σε 32bit σύστημα, θα δώσει 4 bytes) Ο τύπος char έχει μέγεθος 1 bytes. Ο τύπος long έχει μέγεθος 4 bytes. Ο τύπος double έχει μέγεθος 8 bytes.

Εισαγωγή στις Συμβολοσειρές Ακολουθεί ένα πρόγραμμα που κάνει έναν διάλογο με τον χρήστη. /* prog09.c – ζητούνται στοιχεία χρήστη */ #include <stdio.h> main() { float weight; int size, letters; char name[40]; printf("Γεια σου! Πώς λέγεσαι; \n"); scanf("%s", name); printf("%s, Ποιο είναι το βάρος σου σε κιλά; \n", name); scanf("%f", &weight); size = sizeof name; letters = strlen(name); printf("Γεια σου, %s, το βάρος σου είναι %2.2f κιλά. \n", name, volume); printf("Επίσης, το σου όνομά σου έχει %d γράμματα, \n", letters); printf("και χρειάζονται %d bytes για την αποθήκευσή του.\n", size); } Τρέχοντας αυτό το πρόγραμμα, παίρνουμε τα εξής αποτελέσματα : Γεια σου! Πώς λέγεσαι; Κώστας Κώστας, ποιο είναι το βάρος σου σε κιλά; 100 Γεια σου, Κώστας, το βάρος σου είναι 100,00 κιλά. Επίσης, το όνομά σου έχει 6 γράμματα, και χρειάζονται 40 bytes για για την αποθήκευσή του. Τα κυριότερα νέα χαρακτηριστικά αυτού του προγράμματος είναι τα εξής :

Page 12: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

1. Έχουμε χρησιμοποιήσει έναν πίνακα που περιέχει μια συμβολοσειρά και συγκεκριμένα κάποιο όνομα. 2. Χρησιμοποιήσαμε τον προσδιοριστή μετατροπής %s για να χειριστούμε την είσοδο και την έξοδο μιας συμβολοσειράς. Η μεταβλητή name, σε αντίθεση με την weight, δεν χρησιμοποιεί το πρόθεμα & όταν χρησιμοποιείται με την scanf(). 4. Χρησιμοποιήσαμε τη συνάρτηση της C strlen() για να βρούμε το μήκος μιας συμβολοσειράς. Τι είναι, όμως, μια συμβολοσειρά; Μια συμβολοσειρά είναι μια σειρά από έναν ή περισσότερους χαρακτήρες, όπως για παράδειγμα : "Τo TEI Μεσoλoγγίoυ ιδρύθηκε τo 1981". Τα διπλά εισαγωγικά δεν είναι μέρος της συμβολοσειράς και υπάρχουν για να οριοθετούν τη συμβολοσειρά, όπως ακριβώς τα απλά εισαγωγικά χρησιμοποιούνται για να οριοθετούν έναν χαρακτήρα. Η C αποθηκεύει τις συμβολοσειρές σ’ έναν πίνακα τύπου char με κάθε χαρακτήρα της συμβολοσειράς να είναι αποθηκευμένος σε κάθε κελί. Η C χρησιμοποιεί τον χαρακτήρα \0 στην τελευταία θέση του πίνακα για να σημειώνει το τέλος μιας συμβολοσειράς. Αυτός ο χαρακτήρας δεν είναι το ψηφίο 0, αλλά είναι ο μη απεικονιζόμενος χαρακτήρας του οποίου ο κώδικας ASCII είναι 0. Ο πίνακας λοιπόν πρέπει να έχει τουλάχιστον ένα περισσότερο κελί από τον αριθμό των χαρακτήρων που πρόκειται να αποθηκευτούν. Η δήλωση ενός πίνακα μπορεί να γίνει με την εξής εντολή : char name[40]; απ' όπου καταλαβαίνουμε ότι η μεταβλητή name είναι πίνακας με μέγεθος 40 και το κάθε στοιχείο της είναι ένας χαρακτήρας.

Η Χρήση των Συμβολοσειρών Δείτε το παρακάτω πρόγραμμα : /* prog10.c – πρόγραμμα με συμβολοσειρές */ #include <stdio.h> #define PRAISE "Έχεις ένα σπουδαίο όνομα!" main() { char name[50]; printf("Πώς σε λένε;\n"); scanf("%s", name); printf("Γεια σου, %s. %s\n", name, PRAISE); } Η έξοδος του προγράμματος θα είναι : Πώς σε λένε; Μάκη Παπαδόπουλο Γεια σου, Μάκη. Έχεις ένα σπουδαίο όνομα! Παρατηρούμε ότι η scanf() διαβάζει μόνο το μικρό όνομα του Παπαδόπουλου. Η συνάρτηση σταματάει λοιπόν μόλις βρει το πρώτο κενό διάστημα. Γενικά, η scanf() χρησιμοποιούμενη με το %s διαβάζει απλές λέξεις και όχι φράσεις σαν συμβολοσειρές. Η C έχει άλλες συναρτήσεις για διάβασμα εισόδου, όπως την gets(), για να χειρίζεται γενικευμένες συμβολοσειρές, που θα τις δούμε αργότερα. Η συμβολοσειρά "x" δεν είναι το ίδιο με τον χαρακτήρα 'x' και αυτό γιατί η 'x' είναι ένας βασικός τύπος (char), ενώ η "x" είναι ένας παραγόμενος τύπος, ένας πίνακας από στοιχεία τύπου char. Ακόμη, η "x" αποτελείται από δύο χαρακτήρες, τον 'x' και τον μηδενικό χαρακτήρα. Η συνάρτηση srtlen() μας δίνει το μήκος μιας συμβολοσειράς σε χαρακτήρες, ενώ ο τελεστής sizeof επιστρέφει τον αριθμό των συνολικών κελιών μνήμης που είχαν δηλωθεί αρχικά γι αυτή τη μεταβλητή.

Page 13: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Οι Σταθερές και ο Προεπεξεργαστής της C Ξέρουμε από τις άλλες γλώσσες προγραμματισμού τη σημασία που έχει η δήλωση μιας σταθεράς σ’ ένα πρόγραμμα. Στη C υπάρχουν δύο τρόποι για να δηλωθεί μια σταθερή τιμή. Ένας τρόπος είναι να ορίσουμε μια μεταβλητή και να την εξισώσουμε με την επιθυμητή σταθερά, ως εξής : float taxrate; taxrate = 0.2; Μ’ αυτόν τον τρόπο η αντικατάσταση της τιμής της μεταβλητής taxrate θα γίνεται όταν το πρόγραμμα τρέχει. Ο προεπεξεργαστής της C διαθέτει έναν καλύτερο τρόπο, όπου απλά προσθέτουμε μια γραμμή στην αρχή του αρχείου που περιέχει το πρόγραμμά μας, ως εξής : #define TAXRATE 0.2 Όταν το πρόγραμμα μεταγλωττιστεί, η τιμή 0.2 θα αντικατασταθεί παντού όπου έχει χρησιμοποιηθεί η TAXRATE. Έτσι, όταν τρέξουμε το πρόγραμμα, όλες οι αντικαταστάσεις θα έχουν ήδη γίνει. Πρέπει να προσέξουμε τη σύνταξη της #define, στην οποία δεν χρησιμοποιείται το ελληνικό ερωτηματικό (;), αφού δεν είναι μια πρόταση της C και ακόμη αποτελεί παράδοση στη C να γράφονται όλες οι σταθερές με κεφαλαία γράμματα για να τις αναγνωρίζουμε αμέσως και να τις ξεχωρίζουμε έτσι από τις άλλες μεταβλητές. Ακολουθεί ένα παράδειγμα με χρήση της #define. /* prog11.c – χρήση της σταθεράς π = 3.14159 */ #include <stdio.h> #define PI 3.14159 main() { float area, circum, radius; printf("Δώστε την ακτίνα του κύκλου : \n"); scanf("%f", &radius); area = PI * radius * radius; circum = 2.0 * PI * radius; printf("Τα στοιχεία του κύκλου είναι :\n"); printf("Περίμετρος = %1.2f, εμβαδόν = %1.2f\n", circum, area); } Το αποτέλεσμα θα είναι : Δώστε την ακτίνα του κύκλου : 6.0 Τα στοιχεία του κύκλου είναι : Περίμετρος = 37.70, εμβαδόν = 113.10 Η εντολή #define μπορεί να χρησιμοποιηθεί τόσο για χαρακτήρες όσο και για σταθερές συμβολοσειρές. Χρησιμοποιούμε απλά εισαγωγικά για την πρώτη περίπτωση και διπλά για τη δεύτερη. Ακολουθούν παραδείγματα : #define TEE 'T' #define ESC '\033' #define OOPS "Τώρα το πέτυχες" Αν κατά λάθος γράψουμε #define TOES = 20, τότε η TOES θα αντικατασταθεί με το = 20 και όχι με το 20.

Τυπώνοντας Μεγάλες Συμβολοσειρές Αν θέλουμε να κόψουμε μια συμβολοσειρά που είναι πολύ μεγάλη για να χωρέσει σε μια γραμμή, υπάρχουν τρεις τρόποι, όπως φαίνεται στο παρακάτω παράδειγμα :

Page 14: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

/* prog12.c – τυπώνοντας μεγάλες συμβολοσειρές */ #include <stdio.h> main() { printf("Να ένας τρόπος να τυπώσετε μια "); printf("μεγάλη συμβολοσειρά.\n"); printf("Να ένας άλλος τρόπος να τυπώσετε μια \ μεγάλη συμβολοσειρά.\n"); printf("Να ο πιο καινούργιος τρόπος να τυπώσετε μια " " μεγάλη συμβολοσειρά.\n"); } Το αποτέλεσμα θα είναι : Να ένας τρόπος να τυπώσετε μια μεγάλη συμβολοσειρά. Να ένας άλλος τρόπος να τυπώσετε μια μεγάλη συμβολοσειρά. Να ο πιο καινούργιος τρόπος να τυπώσετε μια μεγάλη συμβολοσειρά.

Page 15: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Μέρος 2: Οι Bασικές Εντολές της C

Οι Βασικοί Τελεστές της C Η C, όπως όλες οι γλώσσες προγραμμστισμού, χρησιμοποιεί τελεστές για να εκτελέσει τις αριθμητικές λειτουργίες. Το = είναι ένας τελεστής καταχώρησης τιμών. Πρέπει να έχουμε υπόψη μας ότι δεν μπορούμε να καταχωρήσουμε τιμή σε μια σταθερά και το μέρος στα αριστερά του συμβόλου = πρέπει να είναι το όνομα της μεταβλητής και αναφέρεται σε μία θέση αποθήκευσης. Εφαρμογές της εντολής αυτής είναι οι εξής : year = 2006; i = i + 1; timi01 = timi02 = timi03 = 68; Στην τελευταία εντολή οι καταχωρήσεις γίνονται από τα δεξιά προς τα αριστερά. Υπάρχουν ακόμα και οι τελεστές πρόσθεσης +, αφαίρεσης -, πολλαπλασιασμού * και διαίρεσης /. Στη διαίρεση πρέπει να έχουμε υπόψη μας ότι η διαίρεση με αριθμούς κινητής υποδιαστολής δίνει αποτέλεσμα του ίδιου τύπου, ενώ η διαίρεση με ακεραίους δίνει μια ακέραια απάντηση. Έτσι, αν η διαίρεση ακεραίων δεν είναι τέλεια, η C απορρίπτει το δεκαδικό μέρος του πηλίκου χωρίς να το στρογγυλοποιεί. Αυτή η διαδικασία λέγεται αποκοπή. Όταν ανακατεύουμε ακεραίους με αριθμούς κινητής υποδιαστολής, το αποτέλεσμα είναι αριθμός κινητής υποδιαστολής. Ακολουθεί ένα ερμηνευτικό παράδειγμα : /* prog13.c – οι διαιρέσεις στη C */ #include <stdio.h> main() { printf("ακέραια διαίρεση : 5/3 είναι %d \n", 5/3); printf("ακέραια διαίρεση : 8/4 είναι %d \n", 8/4); printf("ακέραια διαίρεση : 7/5 είναι %d \n", 7/5); printf("διαίρεση κινητής υποδιαστολής : 7./4. είναι %1.2f \n", 7./4.); printf("μικτή διαίρεση : 7./4 είναι %1.2f \n", 7./4); } Το αποτέλεσμα θα είναι : ακέραια διαίρεση : 5/3 είναι 1 ακέραια διαίρεση : 8/4 είναι 2 ακέραια διαίρεση : 7/5 είναι 1 διαίρεση κινητής υποδιαστολής : 7./4. είναι 1.75 μικτή διαίρεση : 7./4 είναι 1.75 Βλέπουμε ότι όσον αφορά τις τρεις πρώτες διαιρέσεις που γίνονται μεταξύ ακεραίων αριθμών, σαν αποτέλεσμα παίρνουμε το πηλίκο της ακέραιας διαίρεσης, κάτι που μας είναι χρήσιμο σε πολλές εφαρμογές, ο αντίστοιχος τελεστής δηλ. του div της Pascal. Βλέπουμε ακόμη ότι από το ανακάτεμα ακεραίων και αριθμών κινητής υποδιαστολής, παίρνουμε αριθμό κινητής υποδιαστολής.

Οι Προτεραιότητες των Τελεστών Η C τοποθετεί κάθε τελεστή σε κάποιο επίπεδο προτεραιότητας. Ο πολλαπλασιασμός και η διαίρεση ανήκουν σε υψηλότερο επίπεδο προτεραιότητας από την πρόσθεση και την αφαίρεση, γι' αυτό και εκτελούνται πρώτα. Αν, όμως, τελεστές του ίδιου επιπέδου προτεραιότητας επιδρούν στον ίδιο τελεστέο, τότε εκτελούνται με τη σειρά εμφάνισής τους στην πρόταση. Για τους περισσότερους τελεστές, η σειρά εκτέλεσης είναι από αριστερά προς τα δεξιά και ο τελεστής = αποτελεί εξαίρεση. Δηλαδή, στην πρόταση :

Page 16: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

a = 25.0 + 60.0 * b / c; γίνεται πρώτα ο πολλαπλασιασμός 60.0 * b, μετά η διαίρεση αυτού με τη μεταβλητή c και τέλος προστίθεται το 25.0. Αν, όμως, θελήσουμε να γίνει πρώτα η πρόσθεση και μετά η διαίρεση, τότε πρέπει να βάλουμε παρενθέσεις, ως εξής : a = (25.0 + 60.0 * b) / c;

Ο Τελεστής sizeof Ο τελεστής αυτός επιστρέφει το μέγεθος σε bytes του τελεστέου του. Ο τελεστέος μπορεί να είναι το όνομα μιας μεταβλητής ή μπορεί να είναι ένας τύπος. Αν είναι τύπος ονόματος, τότε πρέπει ο τελεστέος να μπει μέσα σε παρενθέσεις, αλλιώς οι παρενθέσεις είναι προαιρετικές. Ακολουθεί ένα παράδειγμα : /* prog14.c - ο τελεστής sizeof */ #include <stdio.h> main() { int n=10; printf("Ο n έχει %d bytes, όλοι οι ακέραιοι έχουν %d bytes. \n", sizeof n, sizeof(int)); } Το αποτέλεσμα θα είναι : Ο 10 έχει 2 bytes, όλοι οι ακέραιοι έχουν 2 bytes.

Ο Τελεστής Ακεραίου Υπολοίπου % Ο τελεστής ακεραίου υπολοίπου (%) χρησιμοποιείται στην αριθμητική των ακεραίων και επιστρέφει το υπόλοιπο της ακέραιας διαίρεσης του ακεραίου στα αριστερά με τον ακέραιο στα δεξιά. Για παράδειγμα, η πράξη 13 % 5 δίνει σαν αποτέλεσμα την τιμή 3, αφού το 5 χωράει δύο φορές στο 13 και έχει υπόλοιπο 3. Ακολουθεί ένα παράδειγμα : /* prog15.c – μετατρέπει τα δευτερόλεπτα σε λεπτά και σε δευτερόλεπτα */ #include <stdio.h> #define SEC_PER_MIN 60 /* 60 δευτερόλεπτα σ’ ένα λεπτό */ main() { int sec, min, left; printf("Μετατροπή δευτερολέπτων σε λεπτά και δευτερόλεπτα \n"); printf("Δώστε τον αριθμό των δευτερολέπτων : \n"); scanf("%d", &sec); /* διάβασμα του αριθμού των δευτερολέπτων */ min = sec / SEC_PER_MIN; /* αριθμός λεπτών */ left = sec % SEC_PER_MIN; /*αριθμός δευτερολέπτων που έμειναν*/ printf(" %d δευτερόλεπτα είναι %d λεπτά και %d δευτερόλεπτα.\n", sec, min, left); } Το αποτέλεσμα θα είναι : Μετατροπή δευτερολέπτων σε λεπτά και δευτερόλεπτα Δώστε τον αριθμό των δευτερολέπτων : 152 152 δευτερόλεπτα είναι 2 λεπτά και 32 δευτερόλεπτα.

Page 17: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Όπως είδαμε στο παραπάνω παράδειγμα, για να υπολογίσουμε πόσα λεπτά υπάρχουν σ’ έναν αριθμό δευτερολέπτων sec, παίρνουμε το πηλίκο της ακέραιας διαίρεσης του sec με το SEC_PER_MIN, που είναι ουσιαστικά το 60 και για να βρούμε πόσα δευτερόλεπτα περίσσευσαν που δεν χώρεσαν σ’ ένα λεπτό, παίρνουμε το υπόλοιπο της ακέραιας διαίρεσης του sec με το SEC_ PER_MIN.

Ο Τελεστής Αύξησης ++ Ο τελεστής αύξησης (++) αυξάνει κατά ένα την τιμή του τελεστέου. Ο τελεστής αυτός υπάρχει σε δύο μορφές : Το ++ μπορεί να βρίσκεται πριν από την επηρεαζόμενη μεταβλητή και ονομάζεται τελεστής προγενέστερης αύξησης ή μετά απ’ αυτήν, οπότε ονομάζεται τελεστήςμεταγενέστερης αύξησης. Τα δύο είδη αυξήσεων διαφέρουν στον χρόνο που γίνεται η αύξηση. Πρώτα, όμως, θα δούμε τις ομοιότητές τους και αργότερα τις διαφορές. Ακολουθεί ένα παράδειγμα : /* prog16.c – χρήση του τελεστή αύξησης ++ */ #include <stdio.h> main() { int a = 0, b = 0; while (a < 5) { a++; ++b; printf("b = %d, a = %d \n", b, a); } } Το αποτέλεσμα θα είναι : b = 1, a = 1 b = 2, a = 2 b = 3, a = 3 b = 4, a = 4 b = 5, a = 5 Θα μπορούσαμε να πάρουμε το ίδιο αποτέλεσμα χρησιμοποιώντας τις εξής εντολές : b = a + 1; a = a + 1; Ο λόγος που χρησιμοποιούμε αυτόν τον τελεστή αύξησης είναι ότι έχει κάποια πλεονεκτήματα που θα φανούν αργότερα. Και για να γίνουμε πιο σαφείς, ακολουθεί ένα παράδειγμα : euro = 2.0; while (++euro < 100.00) { draxmes = 340.75 * euro; printf("%10.2f %20.2f \n", euro, draxmes); } Εδώ έχουμε συνδυάσει τη διαδικασία αύξησης και σύγκρισης του βρόχου while σε μία μόνο έκφραση, δηλ. η τιμή της μεταβλητής euro πρώτα αυξάνεται κατά 1 και αμέσως μετά συγκρίνεται με το 100.00. Έτσι, έχουμε τον έλεγχο του βρόχου και την αύξηση της μεταβλητής στο ίδιο μέρος. Ας δούμε ακόμα ένα παράδειγμα : /* prog17.c – προγενέστεροι και μεταγενέστεροι */ #include <stdio.h>

Page 18: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

main() { int a =1, b = 1; int aplus, plusb; aplus = a++; /* μεταγενέστερος */ plusb = ++b; /* προγενέστερος */ printf("a aplus b plusb \n"); printf("%1d %5d %5d %5d\n", a, aplus, b, plusb); } Το αποτέλεσμα θα είναι : a aplus b plusb 2 1 2 2 Τόσο η a όσο και η b αυξάνονται κατά ένα. Επομένως, η aplus έχει την τιμή της a πριν η a αλλάξει και η plusb έχει την τιμή της b μετά την αλλαγή της b. aplus = a++;

/* μεταγενέστερη : η a άλλαξε μετά τη χρήση της τιμής της */ plusb = ++b; /* προγενέστερη : η b άλλαξε πριν τη χρήση της τιμής της */

Αν ένας από τους τελεστές αύξησης χρησιμοποιείται μόνος του σε κάποια πρόταση, τότε δεν έχει σημασία ποια μορφή θα χρησιμοποιήσουμε. Η επιλογή, όμως, έχει σημασία όταν ο τελεστής και ο τελεστέος αποτελούν μέρη κάποιας έκφρασης, όπως μόλις είδαμε. Για να συνοψίσουμε, το n++ σημαίνει : "κάνε χρήση του n και μετά αύξησέ το" και το ++n σημαίνει : "αύξησε το n και μετά χρησιμοποίησέ το".

Ο Τελεστής Μείωσης - - Δεν θα ήταν δυνατόν φυσικά να μην υπήρχε και ο αντίστοιχος τελεστής μείωσης, δηλ. αντί για το ++ χρησιμοποιούμε το - -. - -count; /* τελεστής προγενέστερης μείωσης */ count- -; /* τελεστής μεταγενέστερης μείωσης */ Ακολουθεί κι ένα παράδειγμα με την χρήση του τελεστή μείωσης : /* prog18.c – χρήση του τελεστή μείωσης */ #include <stdio.h> #define MAX 100 main() { int count = MAX + 1; while ( --count > 0) { printf("Υπάρχουν %d βιβλία στη βιβλιοθήκη \n", count); printf("Πάρε ένα βιβλίο, \n"); printf("%d βιβλία έμειναν στη βιβλιοθήκη \n\n", count-1); } } Το αποτέλεσμα θα είναι : Υπάρχουν 100 βιβλία στη βιβλιοθήκη Πάρε ένα βιβλίο,

Page 19: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

99 βιβλία έμειναν στη βιβλιοθήκη Υπάρχουν 99 βιβλία στη βιβλιοθήκη Πάρε ένα βιβλίο, 98 βιβλία έμειναν στη βιβλιοθήκη

...

Οι τελεστές αύξησης και μείωσης έχουν υψηλή προτεραιότητα και μόνο οι παρενθέσεις έχουν υψηλότερη. Πρέπει πάντως να αποκτήσει κανείς αρκετή εμπειρία με τους τελεστές αύξησης και μείωσης, να μάθει τις ιδιαιτερότητες της έκδοσης της C με την οποία δουλεύει και μετά να τους χρησιμοποιεί. Πρέπει να έχουμε υπόψη μας τα εξής :

Δεν πρέπει να χρησιμοποιούμε τελεστές αύξησης ή μείωσης σε μεταβλητή που αποτελεί μέρος περισσότερων του ενός ορισμάτων ή συναρτήσεων.

Δεν πρέπει να χρησιμοποιούμε τελεστές αύξησης ή μείωσης σε μεταβλητή που εμφανίζεται περισσότερες από μία φορές σε μια έκφραση.

Δηλαδή, οι παρακάτω εκφράσεις μπορεί να μπερδέψουν τη C και πάντως κανείς δεν εγγυάται για το αποτέλεσμά τους : printf("%10d %10d\n", num, num*num++); a = num/2 + 5*(1 + num++); b = n++ + n++;

Εκφράσεις και Προτάσεις Μια έκφραση αποτελείται από έναν συνδυασμό τελεστών και τελεστέων (ο τελεστέος είναι αυτό, πάνω στο οποίο δρα ο τελεστής). Η απλούστερη έκφραση είναι ένας μόνο τελεστέος. Ακολουθούν μερικές εκφράσεις : 4 4 + 21 a*(b + c/d)/20 q = 5*2 x = ++q % 3 q > 3 Οι τελεστέοι μπορεί να είναι σταθερές, μεταβλητές ή συνδυασμοί αυτών των δύο. Κάθε έκφραση στη C έχει μια τιμή και για να βρούμε την τιμή αυτή, εκτελούμε τις πράξεις με τη σειρά που υποδεικνύεται από την προτεραιότητα των τελεστών. Εκφράσεις σχέσεων, όπως η q > 3, έχουν τιμή 1 αν είναι αληθείς και 0 αν είναι ψευδείς. Ακολουθούν μερικές εκφράσεις : Έκφραση Τιμή -4+6 2 c = 3 + 8 11 5>3 1 6+(c=3+8) 17 Οι προτάσεις είναι τα πρωταρχικά δομικά στοιχεία ενός προγράμματος, δηλ. το πρόγραμμα είναι μια σειρά προτάσεων και σημείων στίξης. Στη C οι προτάσεις κλείνουν με το σύμβολο ;. Έτσι, η a = 4 είναι μια έκφραση, αλλά η a= 4; είναι μια πρόταση. Στο παράδειγμα : x = 6 + (y=5); η υποέκφραση y=5 είναι μια ολοκληρωμένη εντολή, αλλά απλά είναι μέρος μιας πρότασης. Επειδή συνεπώς μια ολοκληρωμένη εντολή δεν είναι και απαραίτητα μια πρόταση, το σύμβολο (;) χρειάζεται για να χαρακτηρίζει τις εντολές που είναι αληθινές προτάσεις. Στο επόμενο παράδειγμα χρησιμοποιούμε τέσσερα είδη προτάσεων: /* prog19.c – τα τέσσερα είδη προτάσεων */

Page 20: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

#include <stdio.h> main() /* βρίσκει το άθροισμα των 20 πρώτων ακεραίων */ { int count, sum; /* πρόταση δήλωσης */ count = 0; /* πρόταση καταχώρησης */ sum = 0; /* πρόταση καταχώρησης */ while (count++ < 20) /* πρόταση ελέγχου while */ sum = sum + count; /* πρόταση */ printf("άθροισμα = %d\n", sum); /* πρόταση - συνάρτηση */ } Μια σύνθετη πρόταση αποτελείται από δύο ή περισσότερες προτάσεις που περικλείονται από αγκύλες. Λέγεται επίσης και μπλοκ (block). Ακολουθούν παραδείγματα : /* τμήμα προγράμματος 1 */ index = 0; while (index++ < 10) sum = 10*index + 2; printf("sum = %d\n", sum); /* τμήμα προγράμματος 2 */ index = 0; while (index++ < 10) { sum = 10*index + 2; printf("sum = %d\n", sum); } Στο τμήμα προγράμματος 1, ο βρόχος while περιλαμβάνει μόνο μια πρόταση αντικατάστασης, δηλ. όταν λείπουν τα { και }, μια πρόταση while τρέχει από το while μέχρι το επόμενο σύμβολο ;. Στο τμήμα προγράμματος 2, τα σύμβολα { και } δηλώνουν ότι και οι δύο προτάσεις αποτελούν μέρος του βρόχου while. Ολόκληρη η σύνθετη πρόταση θεωρείται σαν μια απλή πρόταση με την έννοια της δομής της πρότασης while.

Οι Μετατροπές Τύπου Η C δεν γκρινιάζει τόσο εύκολα όσο η Pascal όταν ανακατεύουμε μεταβλητές και σταθερές διαφορετικών τύπων δεδομένων. Η C χρησιμοποιεί ορισμένους κανόνες για να μετατρέψει αυτόματα τους τύπους : 1. Όταν εμφανίζονται σε εκφράσεις, τόσο ο τύπος char όσο και ο τύπος short, με πρόσημο ή χωρίς, αυτόματα μετατρέπονται σε τύπο int. Επειδή αυτές είναι μετατροπές προς κάποιο μεγαλύτερο τύπο, λέγονται προαγωγές. 2. Σε κάθε πράξη όπου εμπλέκονται δύο τύποι, οι δύο τιμές μετατρέπονται στον τύπο αυτής με τον "υψηλότερο" βαθμό. 3. Η ιεραρχία των τύπων από τους υψηλότερους προς τους χαμηλότερους είναι η εξής : long double, double, float, unsigned long, long, unsigned int και int. 4. Σε μια πρόταση καταχώρησης, το τελικό αποτέλεσμα των υπολογισμών μετατρέπεται στον τύπο της μεταβλητής στην οποία καταχωρήθηκε η τιμή. Έτσι, όμως, μπορεί μια τιμή να μετατραπεί σε τύπο χαμηλότερου βαθμού, όταν π.χ. καταχωρούμε τύπο float σε τύπο int και γίνεται, όπως είδαμε, στρογγυλοποίηση του αριθμού. Ακολουθεί ένα παράδειγμα : /* prog20.c – αυτόματη μετατροπή τύπων */ #include <stdio.h>

Page 21: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

main() { char ch; int i; float fl; fl = i = ch = ‘A’; printf("ch = %c, i = %d, fl = %2.2f \n", ch, i, fl); ch = ch + 1; i = fl + 2 * ch; fl = 2.0 * ch + i; printf("ch = %c, i = %d, fl = %2.2f \n", ch, i, fl); } Το αποτέλεσμα θα είναι : ch = A, i = 65, fl = 65.00 ch = B, i = 197, fl = 329.00

Ο Τελεστής Εκμαγείο Όλες οι μετατροπές τύπων που αναφέραμε μέχρι τώρα γίνονται αυτόματα. Όμως, είναι πιθανό να θέλουμε να δώσουμε εμείς τις οδηγίες για την ακριβή μετατροπή του τύπου που θέλουμε. Η μέθοδος αυτή λέγεται εκμαγείο και συνίσταται στην τοποθέτηση μπροστά από την ποσότητα του ονόματος, του επιθυμούμενου τύπου μέσα σε παρενθέσεις. Οι παρενθέσεις μαζί με το όνομα του τύπου αποτελούν τον τελεστή-εκμαγείο. Ακολουθούν παραδείγματα, όπου η μεταβλητή m είναι τύπου int : m = 1.6 + 1.7; m = (int) 1.6 + (int) 1.7; Το πρώτο παράδειγμα κάνει αυτόματη μετατροπή και η μεταβλητή m παίρνει την τιμή 3. Το δεύτερο παράδειγμα περιέχει δύο εκμαγεία τύπου int και οι μετατροπές σε ακεραίους γίνονται πριν από την πρόσθεση και έτσι η τιμή της μεταβλητής m είναι 2.

Ορίσματα Συναρτήσεων και Μετατροπές Τύπων Το επόμενο παράδειγμα περιέχει μια συνάρτηση που τυπώνει έναν ορισμένο αριθμό συμβόλων #. Το παράδειγμα αυτό δείχνει ακόμα μερικά σημεία που αφορούν μετατροπές τύπου. /* prog21.c – ορίζει μια συνάρτηση μ’ ένα όρισμα (argument) */ #include <stdio.h> main() { int times = 4; char ch = ‘!’; /* o ASCII κώδικας είναι 33 */ float f = 5.0; f(times); /* όρισμα τύπου int */ f(ch); /* char αυτόματα int */ f((int) f); /* το εκμαγείο αναγκάζει την f int */ } f(n) /* παλιός τρόπος επικεφαλίδας συνάρτησης */ int n; /* η συνάρτηση έχει ένα όρισμα τύπου int */ { while (n- - > 0) printf("#");

Page 22: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

printf("\n"); } Το αποτέλεσμα θα είναι : #### ################################ ##### Εφόσον η συνάρτηση f() παίρνει ένα όρισμα, βάζουμε ένα όνομα μεταβλητής : n. Μετά δηλώνουμε τον τύπο της n. Αυτές οι δηλώσεις ορισμάτων βρίσκονται ανάμεσα στο όνομα της συνάρτησης και στην αρχική αγκύλη. Η δήλωση ενός ορίσματος δημιουργεί μια μεταβλητή που λέγεται τυπικό όρισμα ή τυπική παράμετρος. Στο πρόγραμμά μας, η κλήση f(times) καταχωρεί την τιμή της μεταβλητής times, δηλ. το 4 στην n. Λέμε ότι η κλήση μιας συνάρτησης περνά (μεταβιβάζει) μια τιμή και αυτή η τιμή λέγεται ενεργό όρισμα ή ενεργή παράμετρος. Τα ονόματα μεταβλητών ανήκουν αποκλειστικά στην συνάρτηση, που σημαίνει ότι ένα όνομα που ορίστηκε σε μια συνάρτηση δεν έχει σχέση με το ίδιο όνομα που ορίστηκε κάπου αλλού. Αυτό σημαίνει ότι μπορεί να έχουμε δύο μεταβλητές με το ίδιο όνομα και το πρόγραμμα να εξακολουθεί να διακρίνει ποια είναι ποια. Η τελευταία κλήση f((int) f) χρησιμοποιεί ένα εκμαγείο τύπου για να μετατρέψει το f στον κατάλληλο τύπο γι' αυτό το όρισμα. Αν δεν χρησιμοποιούσαμε αυτήν την μετατροπή, τότε η συνάρτηση θα περίμενε να βρει τιμή τύπου int και θα διάβαζε έτσι μόνο τα 2 bytes από τα 8 bytes της τιμής τύπου float. Έτσι, προσαρμόσαμε τον τύπο της μεταβλητής με τον τύπο του ορίσματος της συνάρτησης.

Η Εντολή While Η γενική μορφή (σύνταξη) της εντολής while είναι η εξής : while (έκφραση) πρόταση Η έκφραση είναι μια σύγκριση τιμών και έχει γενικά ένα λογικό αποτέλεσμα. Η πρόταση μπορεί να είναι μια απλή πρόταση με το σύμβολο ; στο τέλος της ή μια σύνθετη πρόταση κλεισμένη ανάμεσα στις αγκύλες { και }. Αν η έκφραση είναι αληθής (δηλ. γενικότερα μη μηδενική), η πρόταση εκτελείται μία φορά και συνεχίζει μέχρι η έκφραση να γίνει ψευδής (δηλ. γενικότερα μηδενική). Φυσικά, θα πρέπει η τιμή της έκφρασης ελέγχου να αλλάζει τιμή, έτσι ώστε η έκφραση τελικά να γίνει ψευδής και να βγούμε από τον βρόχο. Υπάρχει, βέβαια, και η περίπτωση η έκφραση να είναι ψευδής και να μην μπούμε καθόλου μέσα στο σώμα του βρόχου. Ακολουθεί ένα παράδειγμα με μια εντολή while : while (scanf(“%d“, &num) == 1) ; /* παραλείπει την είσοδο που είναι ακέραιος */ το τμήμα αυτό του προγράμματος παραλείπει (αγνοεί) τις ακέραιες τιμές και συνεχίζει μόνο όταν συναντήσει μια μη ακέραια τιμή.

Ένα Παράδειγμα με την While Θα δούμε τώρα τον βρόχο while σ’ ένα πρόγραμμα που αθροίζει ακέραιες τιμές που εισάγονται από το πληκτρολόγιο. /* prog22.c – αθροίζει ακεραίους που δίνονται διαλογικά */ #include <stdio.h> main() { long num; long sum = 0L; /* αρχική τιμή της sum = 0 */ int status; printf("Δώστε έναν ακέραιο. ");

Page 23: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

printf("Δώστε q για να σταματήσετε. \n "); status = scanf(" %ld", &num); while (status == 1) /* το == σημαίνει ‘ίσο με‘ */ { sum = sum + num; printf("Δώστε τον επόμενο ακέραιο. "); printf("Δώστε q για να σταματήσετε. \n"); status = scanf(“ %ld“, &num); } printf("Οι ακέραιοι αριθμοί έχουν άθροισμα %ld. \n“, sum); } Το αποτέλεσμα θα είναι : Δώστε έναν ακέραιο. Δώστε q για να σταματήσετε. 20 Δώστε τον επόμενον ακέραιο. Δώστε q για να σταματήσετε. 5 Δώστε τον επόμενον ακέραιο. Δώστε q για να σταματήσετε. 30 Δώστε τον επόμενον ακέραιο. Δώστε q για να σταματήσετε. Q Οι ακέραιοι αριθμοί έχουν άθροισμα 55. Ας δούμε λίγες ενδιαφέρουσες λεπτομέρειες του προγράμματος. Κατ’ αρχήν, ο τελεστής == είναι ο τελεστής ισότητας της C, ενώ η εντολή status=1 θα καταχωρούσε το 1 στη μεταβλητή status. Το πρόγραμμα τελειώνει όταν η status πάρει μια τιμή διαφορετική από το 1, οπότε σταματάει ο βρόχος και το πρόγραμμα εμφανίζει την τελική τιμή του αθροίσματος. Βλέπουμε, όμως, ότι με την χρήση της scanf() πετύχαμε δύο πράγματα : διαβάζουμε μια καινούργια τιμή για την μεταβλητή num και ταυτόχρονα κάνουμε χρήση της επιστρεφόμενης τιμής της scanf(). Η scanf() επιστρέφει τον αριθμό των στοιχείων που διάβασε με επιτυχία. Αν, δηλ., διαβάσει έναν ακέραιο, τότε επιστρέφει την τιμή 1, η οποία και καταχωρείται στην μεταβλητή status, αλλιώς αν δώσουμε μια μη αριθμητική είσοδο, όπως το q, τότε κανένα στοιχείο δεν διαβάζεται και η επιστρεφόμενη τιμή της status γίνεται ίση με 0. Έτσι, με την χρήση της scanf() πετύχαμε μαζί με το διάβασμα μιας τιμής και τον έλεγχο της εξόδου από τον βρόχο. Βλέπουμε ακόμη ότι έχουμε μια εντολή scanf() πριν μπούμε στον βρόχο while, για να μπορέσει έτσι να γίνει ο αρχικός έλεγχος της συνθήκης και επίσης μια εντολή scanf() στο τέλος του βρόχου για να μπορούν να συνεχίζονται οι επαναλήψεις. Θα μπορούσαμε να γράψουμε την εντολή while και ως εξής : while (scanf(“%ld”, &num)==1)

Οι Αληθείς και οι Ψευδείς Τιμές Ακολουθεί ένα παράδειγμα : /* prog23.c – οι αληθείς και οι ψευδείς τιμές στη C */ #include <stdio.h> main() { int true, false; true = (5>2); /* τιμή μιας αληθούς σχέσης */ false = (5==2); /* τιμή μιας ψευδούς σχέσης */ printf("αληθής = %d; ψευδής = %d \n", true, false); } Το αποτέλεσμα του προγράμματος θα είναι : αληθής = 1; ψευδής = 0 Δηλαδή, στη C μια αληθής έκφραση έχει τιμή 1 και μια ψευδής έκφραση έχει τιμή 0.

Page 24: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Ο παρακάτω βρόχος δεν σταματάει ποτέ : while (1) { ...

} Από το επόμενο παράδειγμα συμπεραίνουμε ότι στη C αληθείς θεωρούνται όλες οι μη μηδενικές τιμές και μόνο το 0 αναγνωρίζεται σαν ψευδές. Έτσι, ουσιαστικά στη C γίνεται αριθμητικός έλεγχος και όχι έλεγχος αληθούς/ψευδούς. /* prog24.c – οι αληθείς και οι ψευδείς τιμές */ #include <stdio.h> main() { int n = 3; while (n) printf(“%d\n”, n- -); n = -3; while (n) printf(“%2d\n”, n++); } Το αποτέλεσμα θα είναι : 3 2 1 -3 -2 -1 Έτσι, στη C η έκφραση while (a!=0) θα μπορεί να γραφεί και ως εξής : while (a)

Ο Βρόχος For Ο βρόχος for αποφεύγει τα τρία βήματα ενός βρόχου while, δηλ. την απόδοση αρχική τιμής, τον έλεγχο της συνθήκης τερματισμού και την αλλαγή της τιμής, όταν, όπως ξέρουμε από τις άλλες γλώσσες προγραμματισμού, οι επαναλήψεις που πρέπει να κάνουμε είναι ορισμένες σε αριθμό. Στη C, όμως, αυτό δεν είναι υποχρεωτικό. Η εντολή for χρησιμοποιεί τρεις εκφράσεις ελέγχου, χωρισμένες με αγγλικές άνω-τελείες (;), για να ελέγχουν τη διαδικασία της ανακύκλωσης. Η πρόταση αρχικών τιμών εκτελείται μόνο μία φορά πριν κάποια από τις προτάσεις του βρόχου εκτελεστεί. Αν η έκφραση ελέγχου είναι αληθής (ή μη-μηδενική), ο βρόχος εκτελείται μία φορά. Μετά, υπολογίζεται η έκφραση ανανέωσης και η έκφραση ελέγχου ελέγχεται μια φορά ακόμα. Η πρόταση for είναι ένας βρόχος συνθήκης εισόδου, που σημαίνει, όπως αναφέρθηκε προηγουμένως, ότι η απόφαση για μια ακόμα εκτέλεση του βρόχου, παίρνεται πριν την εκτέλεση αυτή. Έτσι, είναι πιθανό ο βρόχος να μην εκτελεστεί ποτέ. Το μέρος των προτάσεων μπορεί να αποτελείται από μια απλή πρόταση ή από μια σύνθετη πρόταση. Η σύνταξη της εντολής for είναι ως εξής : for (αρχική τιμή; Έλεγχος; Ανανέωση) πρόταση Ο βρόχος επαναλαμβάνεται μέχρι ο έλεγχος να γίνει ψευδής ή μηδέν. Ακολουθεί ένα παράδειγμα : /* prog25.c – ένας βρόχος με χρήση της for */ #include <stdio.h>

Page 25: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

#define NUMBER 10 main() { int count; for (count=1; count<=NUMBER; count++) printf("Mesolongi para siempre!\n"); } Οι παρενθέσεις που ακολουθούν τη λέξη-κλειδί for περιέχουν τρεις εκφράσεις χωρισμένες με το σύμβολο ;. Η πρώτη έκφραση δίνει αρχική τιμή και εκτελείται μία φορά μόνο στην αρχή. Η δεύτερη έκφραση είναι μια συνθήκη ελέγχου και υπολογίζεται πριν από κάθε δυναμική εκτέλεση του βρόχου. Ο βρόχος τελειώνει όταν η έκφραση γίνει ψευδής. Η τρίτη έκφραση υπολογίζεται στο τέλος κάθε βρόχου. Η πρόταση for ακολουθείται από μια απλή ή σύνθετη πρόταση. Μπορούμε να καθυστερήσουμε για λίγο το τρέξιμο ενός προγράμματος, ως εξής : for (n=1; N<=10000; N++) ; Η ANSI C διαθέτει και τη συνάρτηση clock(), που μπορεί να χρησιμοποιηθεί για να δημιουργήσουμε χρονικές καθυστέρησης.

Ευέλικτες Χρήσεις της Εντολής For

Ο ατέρμονας βρόχος με την for γίνεται ως εξής : for ( ; ; ). Η εντολή for της C μπορεί να χρησιμοποιήσει και αύξηση διάφορη του 1 :

for (n=0; N<60; N=n+10) Ακόμα, μπορούμε να μετράμε με χαρακτήρες αντί για αριθμούς :

for (ch=’a’; ch<=’z’; ch++) Μπορούμε ακόμα να ελέγχουμε κάποιες άλλες συνθήκες, εκτός από τον γνωστό μας

αριθμό των επαναλήψεων : for (num=1; num*num*num<=216; num++)

Μπορούμε ακόμα να κάνουμε πράξεις με την ποσότητα ελέγχου : for (d=100.0; d<150.0; d=d*1.1)

Μπορούμε γενικά να χρησιμοποιήσουμε οποιαδήποτε νόμιμη έκφραση για την τρίτη έκφραση :

for (x=1; Y<=75; Y=++x*5+50) Η πρώτη έκφραση δεν χρειάζεται να δίνει πάντα αρχική τιμή σε μια μεταβλητή :

for (printf(…); num!=6; scanf(“%d”, &num)) Μπορούμε να έχουμε περισσότερους προσδιορισμούς αρχικής τιμής ή ανανεώσεις,

χωρισμένες με κόμμα, μέσα στον βρόχο for : for (x=1, y=2; X<=16; X++, y=y+3) Συμπεραίνουμε ότι πρόταση for δεν χρησιμοποιείται μόνο για ένα αυστηρά συγκεκριμένο πλήθος επαναλήψεων, αλλά μπορεί να κάνει πολλά περισσότερα πράγματα.

Περισσότεροι Τελεστές Καταχώρησης Έχουμε ήδη διαπιστώσει ότι η C έχει αρκετούς τελεστές κατχώρησης. Ο πιο βασικός είναι ο =, ο οποίος απλά καταχωρεί την τιμή της έκφρασης που βρίσκεται στα δεξιά του στην μεταβλητή που βρίσκεται στα αριστερά του. Οι άλλοι τελεστές καταχώρησης ανανεώνουν μεταβλητές : +=, –=, *=, /=, %=. Κάθε ένας από αυτούς τους τελεστές χρησιμοποιείται μ’ ένα όνομα μεταβλητής στα αριστερά του και μια έκφραση στα δεξιά του. Στην μεταβλητή καταχωρείται μια νέα τιμή ανάλογα με την τιμή της έκφρασης στα δεξιά. Η ακριβής ρύθμιση εξαρτάται από τον τελεστή. Για παράδειγμα : a += 20 είναι το ίδιο όπως a = a + 20

Page 26: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

b –= 2 είναι το ίδιο όπως b = b – 2 c *= 2 είναι το ίδιο όπως c = c * 2 d /= 2.5 είναι το ίδιο όπως d = d / 2.5 e %= 3 είναι το ίδιο όπως e = e % 3 x *= 3 * y +12 είναι το ίδιο όπως x = x * (3 * y + 12)

Ο Βρόχος Do While Είδαμε ότι ο βρόχος while και ο βρόχος for είναι και οι δύο βρόχοι συνθήκης εισόδου. Η C έχει και έναν βρόχο συνθήκης εξόδου, στον οποίο η συνθήκη ελέγχεται μετά από κάθε επανάληψη του βρόχου. Αυτός είναι ο βρόχος do while. Η πρόταση do while δημιουργεί έναν βρόχο που επαναλαμβάνεται μέχρι η συνθήκη ελέγχου να γίνει ψευδής ή μηδέν. Ο βρόχος do while είναι ένας βρόχος συνθήκης εξόδου, δηλ. η απόφαση για μια ακόμα επανάληψη παίρνεται μετά από κάθε εκτέλεση του βρόχου. Έτσι, ο βρόχος πρέπει να εκτελεστεί τουλάχιστον μία φορά. Το μέρος των προτάσεων μπορεί να αποτελείται είτε από μία απλή πρόταση ή από μία σύνθετη. do scanf(“%d”, &number) while (number != 20); Ακολουθεί ένα παράδειγμα : /* prog26.c – βρόχος συνθήκης εξόδου */ #include <stdio.h> main() { char ch; do { scanf("%c", &ch); printf("%c", ch); } while (ch!=’#’); } Βλέπουμε ότι το πρόγραμμα διαβάζει χαρακτήρες και τους τυπώνει μέχρι να εμφανιστεί ο χαρακτήρας #. Θα τυπώσει, όμως, και τον χαρακτήρα #, όταν αυτός εμφανιστεί. Για να αποφύγουμε αυτή την περίπτωση, μπορούμε να χρησιμοποιήσουμε την εντολή if, ως εξής : if (ch<>’#’)

printf("%c", ch); Η γενική μορφή του βρόχου do while είναι η εξής :

do

πρόταση while (έκφραση); Το μέρος πρόταση επαναλαμβάνεται μέχρι η έκφραση να γίνει ψευδής ή μηδέν. Ένας βρόχος do while εκτελείται πάντα τουλάχιστον μία φορά, αφού ο έλεγχος γίνεται μετά την εκτέλεση του σώματος του βρόχου. Από την άλλη μεριά, ένας βρόχος for ή ένας βρόχος while μπορεί να μην εκτελεστεί καμία φορά, αφού ο έλεγχος γίνεται πριν από την εκτέλεση της εντολής. Το φαινομενικό αυτό ελάττωμα που έχει ο βρόχος do while, ότι δηλ. εκτελείται τουλάχιστον μία φορά και ίσως και όταν δεν χρειάζεται, είδαμε ότι πολύ εύκολα μπορεί να διορθωθεί με χρήση της εντολής if μέσα στον βρόχο.

Page 27: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Οι Εσωτερικοί Βρόχοι Εσωτερικός βρόχος λέγεται αυτός που βρίσκεται στο εσωτερικό ενός άλλου βρόχου. Μια συνήθης χρήση των εσωτερικών βρόχων είναι για την απεικόνιση δεδομένων σε γραμμές και στήλες. Ο ένας βρόχος χειρίζεται όλες τις στήλες σε μια γραμμή και ο άλλος όλες τις γραμμές. Ακολουθεί ένα παράδειγμα : /* prog27.c – χρήση εσωτερικών βρόχων */ #include <stdio.h> #define GRAMMES 5 #define STILES 5 main() { int row; char ch; for (row=0; row<GRAMMES; row++) { for (ch=’A’; ch<’A’+STILES; ch++) printf(" %c", ch); printf("\n"); } } Το αποτέλεσμα θα είναι : ABCDE ABCDE ABCDE ABCDE ABCDE Ο πρώτος βρόχος for λέγεται έξω βρόχος και ο άλλος βρόχος for λέγεται μέσα βρόχος. Ο έξω βρόχος αρχίζει με τη row να έχει τιμή 0 και τελειώνει όταν η row γίνει ίση και με το 4. Άρα, κάνει 5 επαναλήψεις. Η πρώτη πρόταση σε κάθε επανάληψη είναι ο άλλος βρόχος for. Αυτός ο βρόχος κάνει επίσης 5 κύκλους, τυπώνοντας τους χαρακτήρες από το Α μέχρι το E στην ίδια γραμμή. Μόλις τελειώσει ο μέσα βρόχος, ακολουθεί μια πρόταση printf("\n"), που όπως ξέρουμε αλλάζει γραμμή. Βλέπουμε συνεπώς, ότι ο μέσα βρόχος τυπώνει 5 χαρακτήρες σε μια γραμμή και ο έξω βρόχος δημιουργεί 5 γραμμές. Μπορεί, όμως, ο μέσα βρόχος να εξαρτάται από τον έξω βρόχο και να συμπεριφέρεται έτσι διαφορετικά σε κάθε επανάληψη. Ακολουθεί το προηγούμενο παράδειγμα τροποποιημένο : /* prog28.c – χρήση εξαρτημένων εσωτερικών βρόχων */ #include <stdio.h> #define GRAMMES 5 #define STILES 5 main() { int row; char ch; for (row=0; row<GRAMMES; row++) { for (ch=’A’+row; ch<’A’+STILES; ch++) printf(" %c", ch); printf("\n");

Page 28: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

} } Το αποτέλεσμα θα είναι : ABCDE BCDE CDE DE E Βλέπουμε, δηλ., ότι αλλάζει σε κάθε εκτέλεση του εσωτερικού βρόχου η αρχική τιμή του βρόχου αυτού, ενώ η συνθήκη ελέγχου και η αύξηση μένουν οι ίδιες.

Οι Πίνακες Γνωρίζουμε την μεγάλη σημασία που έχουν οι πίνακες για τον προγραμματισμό. Μπορούμε να αποθηκεύουμε αρκετά στοιχεία για παρόμοιες πληροφορίες μ’ έναν πολύ βολικό τρόπο. Ένας πίνακας είναι μια σειρά δεδομένων του ίδιου τύπου, όπως π.χ. 15 στοιχεία τύπου char ή 10 στοιχεία τύπου int, αποθηκευμένα ακολουθιακά. Ολόκληρος ο πίνακας έχει ένα απλό όνομα και τα δεδομένα του, που λέγονται στοιχεία του πίνακα, μπορούν να προσπελαστούν με χρήση μιας κατάστασης ακεραίων. Για παράδειγμα, η παρακάτω δήλωση : float pin[20];, δηλώνει ότι ο pin είναι ένας πίνακας με 20 στοιχεία, με πρώτο στοιχείο το pin[0], δεύτερο στοιχείο το pin[1], κοκ μέχρι και το pin[19]. Βλέπουμε ότι η αρίθμηση των στοιχείων του πίνακα αρχίζει από το 0 και όχι από το 1. Σε κάθε στοιχείο του παραπάνω πίνακα μπορούμε να καταχωρήσουμε μια τιμή τύπου float, ως εξής : pin[5] = 20.16; pin[6] = 1.3e+12; Μπορούμε να ορίσουμε και πίνακες άλλων τύπων : int numbers[10]; char alpha[26]; long bignumbers[100]; Οι αριθμοί που χρησιμοποιούνται για να χαρακτηρίζουν τα στοιχεία του πίνακα καλούνται δείκτες του πίνακα, οι οποίοι πρέπει να είναι ακέραιοι και να αρχίζουν από το 0. Τα στοιχεία του πίνακα είναι αποθηκευμένα στην μνήμη, το ένα δίπλα στο άλλο. Ακολουθεί ένα πρόγραμμα με πίνακες και με χρήση της εντολής for, το οποίο διαβάζει 10 βαθμούς, τους εμφανίζει στην οθόνη και μετά υπολογίζει το άθροισμά τους και τον μέσο όρο τους : /* prog29.c – χρήση βρόχων για επεξεργασία πινάκων */ #include <stdio.h> #define SIZE 10 main() { int i, sum, b[SIZE]; float average; printf("Δώστε %d βαθμούς \n", SIZE); for (i = 0; i < SIZE; i++) scanf("%d", &b[i]); /* διάβασμα των 10 βαθμών */ printf("Οι βαθμοί που διαβάστηκαν είναι οι εξής : \n"); for (i = 0; i < SIZE; i++) printf("%5d", b[i]); /* επιβεβαίωση της καταχώρησης */

printf("\n"); for (i = 0; i < SIZE; i++) sum += b[i]; /* άθροιση των βαθμών */ average = (float) sum / SIZE; /* υπολογισμός μέσους όρου */

Page 29: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

printf("Άθροισμα βαθμών = %d, Μέσος Όρος = %.2f \n", sum, average);

} Βλέπουμε ότι ο βρόχος for μάς δίνει την δυνατότητα για έναν απλό, εύκολο και ευθύ τρόπο χειρισμού των δεικτών του πίνακα. Η παρακάτω εντολή είναι πολύ βολική για την επεξεργασία των στοιχείων ενός πίνακα μεγέθους SIZE : for (i=0; i<SIZE; i++)

Βρόχοι και Συναρτήσεις Ακολουθεί ένα πρόγραμμα : /* prog30.c – ύψωση αριθμού σε δύναμη */ #include <stdio.h> main() { double x, xpow; double power(); /* δήλωση της συνάρτησης */ int n; printf("Δώστε έναν αριθμό και μια ακέραια δύναμη "); printf("Δώστε q για να σταματήσετε."); while (scanf("%lf%d", &x, &n) == 2) { xpow = power(x, n); /* κλήση της συνάρτησης */ printf("Το %.3e εις την %d είναι %.3e \n", x, n, xpow); } } double power(a, b) /* ορισμός της συνάρτησης */ double a; int b; { double pow = 1; int i; for (i = 1; i <= b; i++) pow *= a; return pow; /* επιστρέφει την τιμή της pow */ } Εδώ υπολογίζουμε το αποτέλεσμα της ύψωσης ενός αριθμού σε μια ακέραια δύναμη με τη χρήση μιας συνάρτησης. Η συνάρτηση δέχεται δύο τιμές και επιστρέφει μία. Χρησιμοποιούμε ένα όρισμα τύπου double και ένα όρισμα τύπου int, που καθορίζουν ποιος αριθμός θα υψωθεί σε ποια δύναμη. Δηλώνουμε την συνάρτηση μέσα στην main() με τον τύπο της τιμής που επιστρέφει και με την χρήση της λέξης-κλειδί return δείχνουμε την τιμή που θα επιστρέψει η συνάρτηση. Τη συνάρτηση την καλούμε μέσα από το πρόγραμμα με την εξής εντολή : xpow = power(x,n);.

Η Εντολή If Η εντολή if καλείται εντολή διακλάδωσης, γιατί δημιουργεί ένα σημείο διασταύρωσης, από το οποίο το πρόγραμμα έχει να διαλέξει μεταξύ δύο δυνατών κατευθύνσεων που μπορεί να ακολουθήσει. Η γενική μορφή (σύνταξη) της εντολής if είναι η εξής : if (έκφραση)

Page 30: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

πρόταση Εάν η έκφραση είναι αληθής (δηλ. όχι μηδέν), τότε εκτελείται η πρόταση. Αλλιώς, αγνοείται. Η πρόταση μπορεί να είναι μια απλή πρόταση ή μια σύνθετη πρόταση. Η C δίνει επίσης την δυνατότητα επιλογής μεταξύ δύο προτάσεων με την χρήση της δομής if else. Αν η έκφραση είναι αληθής, τότε εκτελείται ό,τι ακολουθεί την if, αλλιώς εκτελείται ό,τι ακολουθεί την else. Η γενική μορφή (σύνταξη) της εντολής if else είναι η εξής : if (έκφραση) πρόταση1 else πρόταση2 Ένα else συνδυάζεται με το πιο κοντινό του if, εκτός κι αν υπάρχουν αγκύλες, που δηλώνουν κάτι άλλο. Υπάρχει η δυνατότητα να χρησιμοποιήσουμε και την εντολή else if, ως εξής : if (έκφραση1) πρόταση1 else if (έκφραση2) πρόταση2 else πρόταση3 Αν η έκφραση1 είναι αληθής, τότε εκτελείται η πρόταση1. Αν η έκφραση1 είναι ψευδής, αλλά η έκφραση2 είναι αληθής, τότε εκτελείται η πρόταση2. Τέλος, αν και οι δύο εκφράσεις είναι ψευδείς, τότε εκτελείται η πρόταση3.

Οι Λογικοί Τελεστές Στο παρακάτω παράδειγμα συνδυάζουμε τρεις σχεσιακές εκφράσεις με τον λογικό τελεστή &&, που είναι το γνωστό μας AND στην C. Το πρόγραμμα μετρά τους μη-λευκούς χαρακτήρες, δηλ. τους χαρακτήρες που δεν είναι κενό, enter και tab. /* prog33.c – μετρά τους μη-λευκούς χαρακτήρες */ #include <stdio.h> #define PERIOD ‘.’ main() { int ch; int charcount = 0; while ((ch = getchar()) != PERIOD) if (ch != ‘ ‘ && ch != ‘\n’ && ch != ‘\t’) charcount++; printf("Υπάρχουν %d μη-λευκοί χαρακτήρες. \n", charcount); } Οι λογικοί τελεστές έχουν μικρότερη προτεραιότητα από τους σχεσιακούς τελεστές και υπάρχουν τρεις απ' αυτούς στην C : Τελεστής Σημασία && And (και) || Or (ή) ! Not (όχι) Ο τελεστής ! έχει πολύ μεγάλη προτεραιότητα, μεγαλύτερη και από εκείνη του πολλαπλασιασμού, ίδια με την προτεραιότητα των σχεσιακών τελεστών και μικρότερη από εκείνη των παρενθέσεων. Ο τελεστής && έχει μεγαλύτερη προτεραιότητα από τον ||, αλλά και οι δύο βρίσκονται κάτω από τους

Page 31: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

σχεσιακούς τελεστές και πάνω από τους τελεστές καταχώρησης. Ακόμη, οι λογικές εκφράσεις στην C υπολογίζονται από αριστερά προς τα δεξιά.

Ο Τελεστής υπό Συνθήκη ? Ένας άλλος τρόπος για να εκφράσουμε την πρόταση if else ονομάζεται έκφραση υπό συνθήκη και χρησιμοποιεί τον τελεστή υπό συνθήκη ? : που έχει τρεις τελεστέους. Το παρακάτω παράδειγμα βρίσκει την απόλυτη τιμή ενός αριθμού : x = (y<0) ? -y : y; Η πρόταση λέει τα εξής : «αν το y είναι μικρότερο από το 0, τότε x=-y, αλλιώς x=y». Η γενική μορφή (σύνταξη) της έκφρασης υπό συνθήκη είναι η εξής : έκφραση1 ? έκφραση2 : έκφραση3 Αν η έκφραση1 είναι αληθής (δηλ. όχι μηδέν), τότε ολόκληρη η έκφραση υπό συνθήκη έχει την τιμή της έκφρασης2, αν όμως η έκφραση1 είναι ψευδής (μηδέν), τότε ολόκληρη η έκφραση υπό συνθήκη έχει την τιμή της έκφρασης3. Πώς βρίσκουμε τον μέγιστο από δύο αριθμούς : max = (a>b) ? a : b Ακολουθούν παραδείγματα : (5>2) ? 1 : 2 έχει τιμή 1 (3>5) ? 1 : 2 έχει τιμή 2 (a>b) ? a : b έχει την τιμή του μεγαλύτερου μεταξύ των a και b

Η Εντολή Switch Η εντολή switch είναι η πλέον κατάλληλη όταν έχουμε να επιλέξουμε από πολλές εναλλακτικές περιπτώσεις, όπου η χρήση διαδοχικών if else δυσκολεύει πολύ τον κώδικα του προγράμματος. Ακολουθεί ένα πρόγραμμα, στο οποίο διαβάζουμε ένα γράμμα και μετά μας δίνει το όνομα μιας πόλης που ν' αρχίζει μ' αυτό το γράμμα. /* prog34.c – χρήση της εντολής switch */ #include <stdio.h> main() { char ch; printf("Δώστε ένα γράμμα του αλφαβήτου."); printf("Πατήστε # για να τελειώσει το πρόγραμμα : \n"); while ((ch = getchar()) != ‘#’) { if (ch >= ‘a’ && ch <= ‘z’) /* δέχεται μόνο */ switch (ch) /* μικρά γράμματα */ { case ‘a’ : printf("Αθήνα \n"); break; case ‘b’ : printf("Βόλος \n"); break; case ‘c’ : printf("Γρεβενά \n"); break; case ‘d’ :

Page 32: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

printf("Δράμα \n"); break; case ‘e’ : printf("Έδεσσα \n"); break; default : printf("Δεν υπάρχει πόλη\n"); } /* τέλος της switch */ else printf("Αναγνωρίζω μόνο μικρά γράμματα \n"); while (getchar() != ‘\n’) ; /* παρέλειψε το υπόλοιπο της γραμμής εισόδου */ printf("Παρακαλώ, δώστε ένα άλλο γράμμα ή #.\n"); } /* τέλος βρόχου while */ } Η εντολή switch δουλεύει ως εξής : Πρώτα, υπολογίζεται η έκφραση μέσα στις παρενθέσεις, που ακολουθεί την switch. Μετά, το πρόγραμμα σαρώνει τον κατάλογο με τις ετικέττες case μέχρι να βρει μια που να ταιριάζει μ’ αυτήν την τιμή. Τότε, το πρόγραμμα πηγαίνει σ' αυτήν την γραμμή και εκτελεί τις εντολές που περιέχονται σ' αυτήν. Αν δεν ταιριάζει με καμία επιλογή, τότε το πρόγραμμα πηγαίνει στην γραμμή με την ετικέττα default, αν υπάρχει αυτή βέβαια. Αν δεν υπάρχει η επιλογή default, τότε το πρόγραμμα συνεχίζει κανονικά στην επόμενη εντολή μετά την switch. Αν δεν υπήρχε η πρόταση break, τότε το πρόγραμμα θα εκτελούσε κάθε πρόταση από την ετικέττα ταύτισης μέχρι και το τέλος της πρότασης switch. Δεν μπορούμε να χρησιμοποιήσουμε μια μεταβλητή για ετικέττα στην πρόταση switch, αλλά μόνο εκφράσεις σταθερών τύπου int ή char. Η δομή της εντολής switch είναι η εξής : switch (ακέραια έκφραση) { case (σταθερά 1 : προτάσεις; (προεραιτικά) case (σταθερά 2 : προτάσεις; (προεραιτικά) ... default : (προεραιτικά) προτάσεις; (προεραιτικά) }

Η Εντολή Break Η εντολή αυτή μπορεί να χρησιμοποιηθεί με τη switch, όπως είδαμε νωρίτερα, αλλά και με τις υπόλοιπες τρεις δομές βρόχου και έχει ως αποτέλεσμα την διακοπή της εκτέλεσης των εντολών switch, for, while και do while και την μετάβαση στο επόμενο στάδιο του προγράμματος. Ακολουθεί ένα κομμάτι προγράμματος που σταματά έναν βρόχο όταν διαβαστεί είτε ένα πλήκτρο enter ή ένα πλήκτρο tab : while ((ch=getchar()) != '\n') { if (ch == '\t') break; putchar(ch); }

Page 33: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Το παραπάνω κομμάτι προγράμματος θα μπορούσε να δουλέψει και ως εξής : while ((ch=getchar()) != '\n' && ch != '\t') putchar(ch);

Η Εντολή Continue Αυτή η εντολή μπορεί να χρησιμοποιηθεί και στα τρία είδη βρόχων, αλλά όχι με την πρόταση switch. Και η continue διακόπτει την ροή ενός προγράμματος, αλλά αντί να τερματίζει ολόκληρο τον βρόχο, έχει ως αποτέλεσμα την μη εκτέλεση του υπόλοιπου τμήματος του βρόχου και την επανάληψή του από την αρχή. Σ’ έναν βρόχο while ή for, ξαναρχίζει ο επόμενος κύκλος του βρόχου, ενώ σ’ έναν βρόχο do while ελέγχεται η συνθήκη εξόδου και μετά, αν χρειαστεί, αρχίζει ο επόμενος κύκλος του βρόχου. Τα δύο παραπάνω κομμάτια προγράμματος θα μπορούσαν να γραφούν τώρα ως εξής : while ((ch=getchar()) != '\n') { if (ch == '\t') continue; putchar(ch); } Δηλαδή ο βρόχος σταματάει τώρα μόνο με το πλήκτρο enter και όχι και με το tab. Το παραπάνω κομμάτι προγράμματος θα μπορούσε να δουλέψει κι έτσι : while ((ch=getchar()) != '\n') if (ch != '\t') putchar(ch);

Αποφυγή Χαρακτήρων Κατά το Διάβασμα Επειδή υπάρχει πάντα ο κίνδυνος ο χαρακτήρας νέας γραμμής <enter> να διαβαστεί ως ξεχωριστός χαρακτήρας και να δημιουργηθούν έτσι προβλήματα σε διαλογικά προγράμματα, μια λύση στο πρόβλημα αυτό είναι με τη χρήση της εντολής while : while (getchar() != '\n') /* αγνοεί το υπόλοιπο της γραμμής εισόδου */ ; Πρέπει να έχουμε υπόψη μας ότι η getchar() διαβάζει κάθε χαρακτήρα, συμπεριλαμβανομένων και των χαρακτήρων κενού διαστήματος (space), στηλοθέτησης (tab) και νέας γραμμής (enter), ενώ η scanf(), όταν διαβάζει αριθμούς, αγνοεί αυτούς τους χαρακτήρες. Μόνο όταν διαβάζουμε χαρακτήρες χρησιμοποιώντας τον προσδιοριστή %c, η scanf() συμπεριφέρεται όπως η getchar(). Για να αποφύγουμε τυχόν προβλήματα με το διάβασμα των χαρακτήρων που προαναφέραμε, μπορούμε να χρησιμοποιήσουμε την εξής εντολή if μέσα στο πρόγραμμά μας : if (ch != '\n' && ch != ' ' && ch != '\t') για να αποφύγουμε έτσι αυτούς τους χαρακτήρες.

Page 34: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Μέρος 3: Συναρτήσεις, Πίνακες και Δείκτες

Εισαγωγή στις Συναρτήσεις Η φιλοσοφία σχεδίασης της C βασίζεται στη χρήση των συναρτήσεων. Έχουμε ήδη δει και χρησιμοποιήσει πολλές συναρτήσεις που έχει το σύστημα, όπως είναι οι printf(), scanf(), strlen() και άλλες, αλλά έχουμε δημιουργήσει και δικές μας συναρτήσεις. Οι πιο πολλές είχαν το όνομα main(). Τα προγράμματα της C πάντα αρχίζουν με την εκτέλεση των εντολών της συνάρτησης main() και μετά η main() καλεί άλλες συναρτήσεις. Ήρθε βέβαια ο καιρός για να δούμε τις συναρτήσεις αναλυτικότερα. Συνάρτηση αποκαλείται μια αυτοδύναμη μονάδα κώδικα προγράμματος που έχει σχεδιασθεί για να εκτελεί μια συγκεκριμένη εργασία. Μια συνάρτηση της C κάνει την ίδια δουλειά με τις συναρτήσεις, τις υπορουτίνες και τις διαδικασίες των άλλων γλωσσών. Μερικές συναρτήσεις, όπως η printf(), έχουν σαν αποτέλεσμα να γίνει μια ενέργεια. Μερικές άλλες συναρτήσεις υπολογίζουν μια τιμή, που χρησιμοποιείται από το πρόγραμμα, όπως η strlen(). Γενικά, μια συνάρτηση μπορεί να παράγει ενέργειες ή και να παράγει τιμές. Χρησιμοποιούμε τις συναρτήσεις για να γλυτώσουμε από τον επαναλαμβανόμενο προγραμματισμό. Δηλαδή, αν πρέπει να γίνει μια συγκεκριμένη εργασία πολλές φορές μέσα σ’ ένα πρόγραμμα, τότε γράφουμε μια φορά μια κατάλληλη συνάρτηση και μετά την χρησιμοποιούμε από το πρόγραμμα όταν και όπου την χρειαζόμαστε. Μπορούμε να χρησιμοποιήσουμε την ίδια συνάρτηση σε διαφορετικά προγράμματα, αλλά ακόμα και αν κάνουμε μια εργασία μόνο μία φορά μέσα στο πρόγραμμα, θα είναι πιο σωστό να χρησιμοποιήσουμε μια συνάρτηση, επειδή έτσι το πρόγραμμα γίνεται πιο εύκολο στην ανάγνωση και στην τροποποίησή του και είμαστε πιο κοντά σ' αυτό που λέγεται δομημένος προγραμματισμός. Για παράδειγμα, αν θέλουμε να κάνουμε ένα πρόγραμμα που να διαβάζει μια λίστα αριθμών, να τους ταξινομεί, να βρίσκει τον μέσο όρο τους και να τυπώνει ένα ραβδόγραμμα, θα μπορούσαμε να δημιουργήσουμε το εξής πρόγραμμα : #include <stdio.h> #define SIZE 50 main() { float list[SIZE]; readlist(list, SIZE); sort(list, SIZE); average(list, SIZE); bargraph(list, SIZE); } Όταν χρησιμοποιούμε περιγραφικά ονόματα για τις συναρτήσεις, είναι ευκολότερο να καταλάβουμε τι κάνει ένα πρόγραμμα και πώς αυτό οργανώνεται. Μπορούμε ακόμα να επεξεργαστούμε ξέχωρα την κάθε συνάρτηση μέχρι να πετύχουμε την σωστή εκτέλεση μιας εργασίας. Ένα ακόμη όφελος είναι ότι αν δημιουργήσουμε τις συναρτήσεις κατά γενικό τρόπο, θα μπορούμε τότε να τις χρησιμοποιήσουμε και σ’ άλλα προγράμματα. Οι συναρτήσεις έχουν σαν σκοπό να μας βοηθήσουν να επικεντρώνουμε την προσοχή μας στην συνολική σχεδίαση ενός προγράμματος και όχι στις λεπτομέρειές του. Πριν να γράψουμε τον κώδικα μιας συνάρτησης, πρέπει να σκεφτούμε πρώτα τι δουλειά πρέπει να κάνει αυτή η συνάρτηση και πώς θα συσχετίζεται με τ’ όλο πρόγραμμα.

Page 35: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Δημιουργία και Χρήση μιας Απλής Συνάρτησης Ακολουθεί ένα πρόγραμμα που χρησιμοποιεί τις συναρτήσεις main() και starbar(). Το πρόγραμμα τυπώνει μια επικεφαλίδα από γράμματα και δύο σειρές από 65 αστεράκια, μία πριν και μία μετά την εκτύπωση των γραμμάτων. Η συνάρτηση main(), δηλ. το κυρίως πρόγραμμα τυπώνει την επικεφαλίδα και η συνάρτηση starbar() έχει αναλάβει τη δουλειά που γίνεται δύο φορές, δηλ. την εκτύπωση της σειράς με τα 65 αστεράκια. /* prog35.c – διαμόρφωση επικεφαλίδας */ #include <stdio.h> #define NAME "Mesolonghi per sempre " #define ADDRESS "Mesolonghi" #define PLACE " Sterea Ellada" #define LIMIT 65 main() { void starbar(); /* δήλωση της συνάρτησης */ starbar(); printf("%s \n", NAME); printf("%s \n", ADDRESS); printf("%s \n", PLACE); starbar(); } void starbar() /* ορισμός της συνάρτησης starbar() */ { int count; for (count = 1; count <= LIMIT; count++) putchar(‘*’); putchar(‘\n’); } Το αποτέλεσμα του προγράμματος θα είναι : ***************************************************************** Mesolonghi per sempre Mesolonghi Sterea Ellada ***************************************************************** Ας δώσουμε λίγη προσοχή στα παρακάτω σημεία του προγράμματος : 1. Η συνάρτηση starbar() καλείται από τη συνάρτηση main() χρησιμοποιώντας απλά τ’ όνομά της. Μετά το όνομα της συνάρτησης και τις παρενθέσεις πρέπει να ακολουθεί το σύμβολο ; , ως εξής : starbar(); Όταν το πρόγραμμα καθώς εκτελείται φθάσει σε μια κλήση συνάρτησης, αναζητά τη συνάρτηση αυτή και ακολουθεί τις εντολές που βρίσκονται σ' αυτήν. Όταν τελειώσει, επιστρέφει στην επόμενη γραμμή του καλούντος προγράμματος. 2. Όταν γράφουμε μια συνάρτηση ακολουθούμε την ίδια μορφή που ξέρουμε από τη main(), δηλ. πρώτα το όνομα, μετά το σύμβολο {, μετά η δήλωση των μεταβλητών, μετά οι προτάσεις ορισμού της συνάρτησης και τέλος το σύμβολο }. 3. Οι παρενθέσεις στη starbar() λένε στον μεταγλωττιστή ότι η starbar() είναι μια συνάρτηση. Η starbar() δεν ακολουθείται από το σύμβολο ;, πράγμα που σημαίνει ότι η συνάρτηση αυτή ορίζεται παρά ότι χρησιμοποιείται.

Page 36: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

4. Η μεταβλητή count στη starbar() είναι μια τοπική μεταβλητή, δηλ. μια μεταβλητή γνωστή μόνο για τη starbar(). Μπορούμε να χρησιμοποιήσουμε το όνομα count και σ’ άλλες συναρτήσεις χωρίς κανένα πρόβλημα, ακόμα και μέσα στην ίδια την main(). 5. Οι συναρτήσεις έχουν κι αυτές τύπους όπως οι μεταβλητές. Ο τύπος void χρησιμοποιείται για συναρτήσεις χωρίς τιμές επιστροφής και ο τύπος αυτός εμφανίζεται στον ορισμό της συνάρτησης. 6. Προγράμματα που χρησιμοποιούν μια συνάρτηση πρέπει να περιέχουν τη δήλωση του τύπου πριν η συνάρτηση χρησιμοποιηθεί. Γι' αυτόν τον λόγο, η main() περιέχει την εξής δήλωση : void starbar(); Έτσι δηλώνουμε ότι το πρόγραμμα χρησιμοποιεί μια συνάρτηση τύπου void που ονομάζεται starbar() και ότι ο μεταγλωττιστής θα βρει τον ορισμό αυτής της συνάρτησης κάπου αλλού. 7. Η συνάρτηση starbar() δεν έχει είσοδο, αφού δεν χρειάζεται καμία πληροφορία από το καλούν πρόγραμμα.

Ορίσματα Συνάρτησης Ας δούμε τώρα πώς θα τροποποιηθεί η συνάρτηση starbar() που χρησιμοποιήσαμε προηγουμένως για να κεντράρει αυτή τη φορά όλους τους τίτλους. Η νέα μας συνάρτηση ονομάζεται n_char() και θα χρησιμοποιήσουμε τώρα ορίσματα γι' αυτή τη συνάρτηση, όπου το ένα θα είναι ο εκτυπούμενος χαρακτήρας και το άλλο ο αριθμός των επαναλήψεων αυτού του χαρακτήρα. Η γραμμή με τους αστερίσκους έχει πλάτος 65 διαστήματα, άρα η συνάρτηση στην περίπτωση αυτή καλείται σαν n_char('*', 65). Η φράση Mesolonghi per sempre έχει πλάτος 21 διαστημάτων, άρα για να την κεντράρουμε θα πρέπει να αφήσουμε 21 κενά διαστήματα από δεξιά και από αριστερά της, δηλ. καλούμε την n_char(' ', 21). /* prog36.c – κεντράρισμα επικεφαλίδας */ #include <stdio.h> #define NAME "Mesolonghi pe sempre " #define ADDRESS "Mesolonghi" #define PLACE " Sterea Ellada" #define LIMIT 65 #define SPACE ‘ ‘ main() { int spaces; void n_char(); n_char(‘*’, LIMIT); /* χρήση σταθερών ως ορίσματα */ putchar(‘\n’); n_char(SPACE, 21); printf("%s \n", NAME); spaces = (65 - strlen(ADDRESS))/2; /* υπολογίζει τα κενά διαστήματα που θα αφήσει */ n_char(SPACE, spaces); /* μια μεταβλητή ως όρισμα */ printf("%s \n", ADDRESS); n_char(SPACE, (65 - strlen(PLACE))/2); /* μια έκφραση ως όρισμα */ printf("%s \n", PLACE); n_char(‘*’, LIMIT); putchar(‘\n’); } void n_char(ch, num) /* ορισμός της συνάρτησης */

char ch; int num;

Page 37: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

{ int count; for (count=1; count<=num; count++) putchar(ch); } Το αποτέλεσμα του προγράμματος θα είναι :

***************************************************************** Mesolonghi per sempre

Mesolonghi Sterea Ellada

***************************************************************** Ας δούμε τον ορισμό της παραπάνω συνάρτησης με ορίσματα αυτή τη φορά : void n_char(ch, num) char ch; int num; Η πρώτη γραμμή μάς πληροφορεί ότι η συνάρτηση έχει δύο ορίσματα, τα ch και num. Η δεύτερη γραμμή δηλώνει τους τύπους των ορισμάτων και πρέπει να παρατηρήσουμε ότι τα ορίσματα δηλώνονται πριν από την πρώτη αγκύλη της συνάρτησης. Και οι δύο αυτές μεταβλητές, ch και num, ονομάζονται τυπικά ορίσματα. Είναι και αυτά τοπικές μεταβλητές της συνάρτησης και μπορούν να χρησιμοποιηθούν και αλλού μέσα στο πρόγραμμα. Για να δώσουμε τιμές στα τυπικά ορίσματα ch και num χρησιμοποιούμε πραγματικά ορίσματα στην κλήση της συνάρτησης. Το τυπικό όρισμα είναι μια μεταβλητή στο πρόγραμμα που καλείται και το πραγματικό όρισμα είναι μια συγκεκριμένη τιμή, που καταχωρείται στη μεταβλητή αυτή από το πρόγραμμα που καλεί. Το πραγματικό όρισμα μπορεί να είναι μια σταθερά, μια μεταβλητή ή ακόμη και μια πιο πολύπλοκη έκφραση. Ο,τιδήποτε κι αν είναι, το πραγματικό όρισμα υπολογίζεται και η τιμή του μεταδίδεται (περνάει) στη συνάρτηση στα αντίστοιχα τυπικά ορίσματα. Η συνάρτηση δεν ενδιαφέρεται, βέβαια, αν αυτή η τιμή που της περνιέται προέρχεται από μια σταθερά, μια μεταβλητή ή μια πιο γενική έκφραση.

Η Χρήση του return Ας δούμε τώρα και την περίπτωση που μια συνάρτηση επιστρέφει μια τιμή στο πρόγραμμα που την κάλεσε. Η επόμενη συνάρτηση imin() επιστρέφει σαν τιμή το μικρότερο από τα δύο ορίσματά της. Ακολουθεί ένα πρόγραμμα : /* prog37.c – βρίσκει τη μικρότερη από δύο τιμές */ #include <stdio.h> main() { int num1, num2; int imin(); while (scanf("%d %d", &num1, &num2) == 2) printf("Το μικρότερο μεταξύ του %d και %d είναι το %d. \n",

num1, num2, imin(num1, num2)); } int imin(n, m) int n, m; { int min; if (n < m)

Page 38: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

min = n; else min = m; return min; } Η λέξη-κλειδί return έχει ως αποτέλεσμα η τιμή οποιασδήποτε έκφρασης που την ακολουθεί, να είναι η τιμή επιστροφής της συνάρτησης. Στη συγκεκριμένη βέβαια περίπτωση, η τιμή επιστροφής της συνάρτησης είναι η τιμή της τοπικής μεταβλητής min. Η συνάρτηση imin() είναι τύπου int, όπως είναι και η μεταβλητή min. Η τιμή επιστροφής μιας συνάρτησης μπορεί να καταχωρηθεί σε μια μεταβλητή ή να χρησιμοποιηθεί σαν μέρος μιας έκφρασης. Ακολουθεί και μια άλλη έκδοση της συνάρτησης imin() : /* δεύτερη έκδοση της συνάρτησης ελάχιστης τιμής */ imin(n, m) int n, m; { return (n<m) ? n : m; } Η χρήση της return έχει και το αποτέλεσμα ότι τερματίζει τη συνάρτηση και ο έλεγχος επιστρέφει στην επόμενη πρόταση του προγράμματος από εκείνη που την κάλεσε. Αυτό συμβαίνει ακόμα και αν η πρόταση return δεν είναι η τελευταία της συνάρτησης. Ακολουθεί και μια τρίτη έκδοση της συνάρτησης imin() : /* τρίτη έκδοση της συνάρτησης ελάχιστης τιμής */ imin(n, m) int n, m; { if (n<m) return (n); else return(m); } Η απλή πρόταση return; προκαλεί τον τερματισμό της συνάρτησης και την επιστροφή του ελέγχου στην καλούσα συνάρτηση. Αυτή η μορφή χρησιμοποιείται σε συναρτήσεις του τύπου void γιατί δεν επιστρέφει καμία τιμή.

Τύποι Συναρτήσεων Και για τις συναρτήσεις πρέπει να δηλώνεται ο τύπος τους, ο οποίος είναι ίδιος με τον τύπο της τιμής επιστροφής της συνάρτησης. Συναρτήσεις χωρίς τιμή επιστροφής δηλώνονται σαν τύπου void και αν δεν δηλωθεί ο τύπος μιας συνάρτησης, τότε η C θεωρεί ότι είναι τύπου int. Η δήλωση του τύπου είναι μέρος του ορισμού της συνάρτησης και αναφέρεται στην τιμή επιστροφής και όχι στα ορίσματα της συνάρτησης. Μια δήλωση της συνάρτησης λέει στο πρόγραμμα τι τύπου είναι η συνάρτηση, ενώ ο ορισμός της συνάρτησης παρέχει τον πραγματικό κώδικά της. Η δήλωση μιας συνάρτησης πρέπει να γίνεται πριν από το σημείο όπου χρησιμοποιείται η συνάρτηση. Στην ANSI C, μπορούμε στη δήλωση μιας συνάρτησης να δηλώνουμε και τον τύπο των μεταβλητών της. Το αποτέλεσμα είναι ένα πρωτότυπο συνάρτησης, δηλ. μια δήλωση συνάρτησης όπου καθορίζονται ο τύπος της τιμής επιστροφής, ο αριθμός των ορισμάτων και ο τύπος των ορισμάτων της συνάρτησης. Ακολουθεί παράδειγμα: int imax(int, int); Έχει ορίσματα 2 ακεραίους και επιστρέφει ακέραιο.

Page 39: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Ο Τελεστής & Μερικές συναρτήσεις, όπως η scanf(), καταχωρούν τιμές σε μεταβλητές του καλούντος προγράμματος και χρησιμοποιούν τον τελεστή διεύθυνσης & στα ορίσματα. Ο τελεστής & μάς δίνει τη διεύθυνση στην οποία αποθηκεύεται μια μεταβλητή. Για παράδειγμα, αν p είναι το όνομα μιας μεταβλητής, τότε &p είναι η διεύθυνση της μεταβλητής p στη μνήμη. Αν η μεταβλητή p έχει τιμή 100 και είναι αποθηκευμένη στη διεύθυνση 15879, τότε η παρακάτω πρόταση : printf("%d %u\n", p, &p); θα δώσει το εξής αποτέλεσμα : 100 15879 Ακολουθεί ένα πρόγραμμα, που χρησιμοποιεί τον τελεστή διεύθυνσης & για να βρει πού φυλάσσονται μεταβλητές με το ίδιο όνομα, οι οποίες ανήκουν σε διαφορετικές συναρτήσεις. /* prog38.c – έλεγχος για το πού βρίσκονται αποθηκευμένες μεταβλητές */ #include <stdio.h> void myfunction(); /* δήλωση συνάρτησης */ main() { int p=2, b=5; printf("Στη main(), p = %d και &p = %p \n", p, &p); printf("Στη main(), b = %d και &b = %p \n", b, &b); myfunction(p); } void myfunction(b); /* ορισμός συνάρτησης */ int b; { int p = 10; printf("Στη mikado(), p = %d και &p = %p \n", p, &p); printf("Στη mikado(), b = %d και &b = %p \n", b, &b); } Χρησιμοποιήσαμε εδώ τη μορφή %p για την εκτύπωση των διευθύνσεων· αν, όμως, το σύστημά μας δεν διαθέτει αυτή τη μορφή, τότε μπορούμε να δοκιμάσουμε με τη μορφή %u. Το αποτέλεσμα του προηγούμενου προγράμματος θα είναι το εξής : Στη main(), p = 2 και &p = FFD8 Στη main(), b = 5 και &b = FFDA Στη myfunction(), p = 10 και &p = FFD0 Στη myfunction(), b = 2 και &b = FFD6 Οι διευθύνσεις εμφανίζονται εδώ σε 16δική μορφή. Από το αποτέλεσμα βλέπουμε ότι οι δύο μεταβλητές p έχουν διαφορετικές διευθύνσεις, όπως και οι δύο μεταβλητές b. Δηλαδή, ο υπολογιστής τις θεωρεί σαν τέσσερις ξεχωριστές μεταβλητές.

Αλλαγή των Μεταβλητών στο Πρόγραμμα που Καλεί Μερικές φορές θέλουμε μια συνάρτηση να προκαλεί αλλαγές στις μεταβλητές μιας άλλης συνάρτησης. Ας πάρουμε το δημοφιλές παράδειγμα της ανταλλαγής των τιμών δύο μεταβλητών x και y. Ξέρουμε ότι αυτό γίνεται με τη χρήση μιας τρίτης βοηθητικής μεταβλητής temp, ως εξής : temp = x; x = y; y = temp;

Page 40: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Χρησιμοποιούμε τα ονόματα x και y για τις μεταβλητές της main() και u και v για τις μεταβλητές της συνάρτησης interchange(). /* prog39.c – ανταλλαγή τιμών μεταβλητών */ #include <stdio.h> void interchange(); main() { int x = 5, y = 10; printf("Αρχικά x = %d και y = %d. \n", x, y); interchange(x, y); printf("Τώρα x = %d και y = %d. \n", x, y); } void interchange(u, v)

int u, v; { int temp; printf("Αρχικά u = %d και v = %d. \n", u, v); temp = u; u = v; v = temp; printf("Τώρα u = %d και v = %d. \n", u, v); } Το αποτέλεσμα του προγράμματος θα είναι το εξής : Αρχικά x = 5 και y = 10 Αρχικά u = 5 και v = 10 Τώρα u = 10 και v = 5 Τώρα x = 5 και y = 10 Βλέπουμε ότι η συνάρτηση interchange() λειτουργεί σωστά, αφού ανταλλάσσει τις τιμές των u και v. Εκείνο που μας ενδιαφέρει, όμως, εμάς είναι η μετάδοση των τιμών αυτών πίσω στη main(). Όπως ξέρουμε, με την πρόταση return μπορούμε να μεταδώσουμε πίσω στο καλούν πρόγραμμα μόνο μία τιμή. Τι μπορούμε, όμως, να κάνουμε; Για να μεταδώσουμε δύο τιμές μιας συνάρτησης στη συνάρτηση που την κάλεσε, πρέπει να χρησιμοποιήσουμε τους δείκτες, που είναι συμβολικές παραστάσεις διευθύνσεων.

Εισαγωγή στους Δείκτες (Pointers) Χρησιμοποιήσαμε προηγουμένως τον τελεστή & για να βρούμε τη διεύθυνση της μεταβλητής p. Η &p δηλαδή είναι ένας δείκτης της p. Η πραγματική διεύθυνση είναι ένας 16δικός αριθμός και η συμβολική παράσταση &p είναι μια σταθερά δείκτη. Η μεταβλητή p δεν πρόκειται να αλλάξει διεύθυνση μέσα στο πρόγραμμα, αν και μπορεί να αλλάξει τιμή. Η C έχει και μεταβλητές δείκτη που έχουν μια διεύθυνση ως τιμή. Ακολουθούν παραδείγματα : ptr = &p; /* καταχωρεί τη διεύθυνση της p στην ptr */ ptr = &b; /* ο δείκτης ptr δείχνει τώρα στη b αντί για την p */ Η ptr δηλαδή είναι μια μεταβλητή και η &p είναι μια σταθερά. Για να δηλώσουμε τον τύπο μιας μεταβλητής δείκτη, πρέπει να χρησιμοποιήσουμε τον τελεστή έμμεσης αναφοράς (*). val = *ptr /* βρίσκει την τιμή που δείχνει ο δείκτης ptr */

Page 41: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Πώς δηλώνουμε, όμως, τους δείκτες; Με τη δήλωση μιας μεταβλητής ότι είναι δείκτης, πρέπει ακόμα να πούμε και τι τύπος είναι η μεταβλητή που δείχνει ο δείκτης και αυτό γιατί διαφορετικοί τύποι μεταβλητών απαιτούν διαφορετικό χώρο αποθήκευσης. Οι δείκτες δηλώνονται ως εξής : int *pi; /* pi είναι ένας δείκτης σε μια ακέραια μεταβλητή */ char *pc; /* pc είναι ένας δείκτης σε μια μεταβλητή χαρακτήρα */ float *pf, *pg; /* pf, pg είναι δείκτες σε μεταβλητές float */ Από τα παραπάνω συμπεραίνουμε ότι pi είναι ο δείκτης και ότι η *pi είναι τύπου int. Παρόμοια, η τιμή *pc την οποία δείχνει ο pc είναι τύπου char.

Οι Δείκτες και οι Συναρτήσεις Ακολουθεί ένα πρόγραμμα όπου γίνεται επιστροφή τιμών στη συνάρτηση που καλεί. /* prog40.c – χρήση δεικτών για την ανταλλαγή τιμών */ #include <sdtio.h> void interchange(); main() { int x = 5, y = 10; printf("Αρχικά x = %d και y = %d. \n", x, y); interchange(&x, &y); /* στείλε τις διευθύνσεις στη συνάρτηση*/ printf("Τώρα x = %d και y = %d. \n", x, y); } void interchange(u, v)

int *u, *v; /* οι u και v είναι τώρα δείκτες */ { int temp; temp = *u; /* η temp παίρνει την τιμή που δείχνει η u */ *u = *v; *v = temp; } Το αποτέλεσμα θα είναι : Αρχικά x = 5 και y = 10 Τώρα x = 10 και y = 5 Ας προσέξουμε τα εξής σημεία : 1. Η κλήση της συνάρτησης γίνεται ως εξής : interchange(&x, &y);, δηλαδή, αντί να στέλνονται οι τιμές των x και y, στέλνονται οι διευθύνσεις τους. Έτσι, τα τυπικά ορίσματα u και v της συνάρτησης interchange() έχουν διευθύνσεις ως τιμές και πρέπει να δηλωθούν ως δείκτες. 2. Εφόσον οι x και y είναι ακέραιοι, δηλώνουμε τις u και v ως δείκτες σε ακέραιους. 3. Θυμηθείτε ότι η u έχει την τιμή &x, έτσι η u δείχνει τη x, δηλαδή η *u μάς δίνει την τιμή του x. 4. Δεν πρέπει να γράψουμε temp = u;, γιατί έτσι θα αποθηκευτεί η διεύθυνση της x και όχι η τιμή της. Θέλουμε μια συνάρτηση να ανταλλάσσει τις τιμές δύο μεταβλητών. Δίνοντας στη συνάρτηση τις διευθύνσεις των μεταβλητών αυτών, αποκτάμε πρόσβαση σ' αυτές τις μεταβλητές. Χρησιμοποιώντας δείκτες και τον τελεστή *, η συνάρτηση μπορεί να εξετάσει τις τιμές που βρίσκονται σ' αυτές τις θέσεις και να τις αλλάξει. Με τους δείκτες δηλαδή μπορούμε να μπούμε στη main() και να αλλάξουμε ό,τι είναι αποθηκευμένο εκεί.

Page 42: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Εισαγωγή στους Πίνακες Ένας πίνακας αποτελείται από μια σειρά στοιχείων ενός τύπου δεδομένων. Το πρόγραμμα πρέπει να γνωρίζει από πόσα στοιχεία αποτελείται ένας πίνακας καθώς και τον τύπο των στοιχείων αυτών. Ο τύπος των στοιχείων ενός πίνακα μπορεί να είναι ένας από τους τύπους μεταβλητών. Ακολουθούν μερικές δηλώσεις πινάκων : main() { float times[365]; /* πίνακας με 365 στοιχεία τύπου float */ char cities[12]; /* πίνακας με 12 στοιχεία τύπου char */ int numbers[50]; /* πίνακας με 50 στοιχεία τύπου int */ } Οι αγκύλες [ και ] μάς λένε ότι πρόκειται για πίνακα και ο αριθμός που είναι μέσα στις αγκύλες μάς δείχνει τον αριθμό των στοιχείων του πίνακα. Για να αναφερθούμε σ’ ένα συγκεκριμένο στοιχείο του πίνακα, χρησιμοποιούμε έναν αριθμό που ονομάζεται δείκτης πίνακακαι που η αρίθμησή του αρχίζει από το 0. Ο πίνακας δηλ. είναι μια μεταβλητή με δείκτη και για να αναφερθούμε πλήρως σ’ ένα στοιχείο του, χρειαζόμαστε το όνομα του πίνακα και την τιμή του δείκτη του πίνακα. Έτσι, π.χ. το times[0] είναι το πρώτο στοιχείο του πίνακα times και το times[364] είναι το 365ο και τελευταίο στοιχείο του.

Αυτόματες, Εξωτερικές και Στατικές Μεταβλητές Μια αυτόματη μεταβλητή ή ένας αυτόματος πίνακας είναι μια μεταβλητή ή ένας πίνακας που ορίζεται μέσα σε μια συνάρτηση. Όπως ήδη ξέρουμε, μια μεταβλητή που ορίζεται μέσα σε μια συνάρτηση (τοπική μεταβλητή), ανήκει σ' αυτή τη συνάρτηση και το όνομά της μπορεί να ξαναχρησιμοποιηθεί και αλλού. Όταν τελειώσει η κλήση της συνάρτησης, τότε απελευθερώνεται ο χώρος της μνήμης που χρησιμοποιήθηκε για τις τοπικές της μεταβλητές. Μπορούμε να δώσουμε αρχικές τιμές σ’ αυτόματους πίνακες ως εξής : main() { int powers[8] = {1, 2, 4, 6, 8, 16, 32, 64}; } Επειδή ο πίνακας ορίζεται μέσα στη main(), είναι ένας αυτόματος πίνακας και η απόδοση αρχικών τιμών σ' αυτόν γίνεται με τη χρήση μιας λίστας τιμών που χωρίζονται με κόμμα και που είναι κλεισμένες σε αγκύλες. Μια εξωτερική μεταβλητή ή ένας εξωτερικός πίνακας είναι μια μεταβλητή ή ένας πίνακας που ορίζεται έξω από μια συνάρτηση. Ακολουθεί ένα παράδειγμα : int report; int numbers[5] = {12, 10, 8, 9, 6}; main() { ... } int feed(n) int n; { ... } Οι εξωτερικές μεταβλητές διαφέρουν από τις αυτόματες στο ότι μπορούν να χρησιμοποιηθούν απ' όλες τις συναρτήσεις ενός προγράμματος, παραμένουν για όσο χρόνο εκτελείται ένα πρόγραμμα και

Page 43: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

δεν χάνονται όταν τελειώνει μια συγκεκριμένη συνάρτηση. Ακόμη, έχουν αρχική τιμή μηδέν όταν δεν ορίζεται κάτι άλλο. Έτσι, η μεταβλητή report στο παραπάνω πρόγραμμα έχει αρχική τιμή ίση με 0. Μια στατική μεταβλητή ή ένας στατικός πίνακας ορίζονται μέσα σε μια συνάρτηση με τη λέξη-κλειδί static : int account(n,m) int n, m; { static int beans[2] = {343, 332}; ... } Μια μεταβλητή που δηλώνεται με τη λέξη-κλειδί static μπορεί να είναι τοπική σε μια συνάρτηση, αλλά διατηρεί τις τιμές της μεταξύ των κλήσεων μιας συνάρτησης και της αποδίδεται αρχική τιμή ίση με 0, όταν βέβαια δεν δηλώνεται κάτι άλλο.

Απόδοση Αρχικών Τιμών σε Πίνακες Ακολουθεί ένα πρόγραμμα που καταχωρεί τον αριθμό των ημερών ανά μήνα σ’ έναν πίνακα με απόδοση αρχικών τιμών και μετά τους εκτυπώνει. /* prog41.c – απόδοση αρχικών τιμών στις ημέρες κάθε μήνα */ #include <stdio.h> #define MONTHS 12 int days[MONTHS] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; main() { int index; for (index=0; index<MONTHS; index++) printf ("Ο μήνας %d έχει %d ημέρες.\n", index+1, days[index]); } Το αποτέλεσμα θα είναι ως εξής : Ο μήνας 1 έχει 31 ημέρες. Ο μήνας 2 έχει 28 ημέρες. Ο μήνας 3 έχει 31 ημέρες. Ο μήνας 4 έχει 30 ημέρες. Ο μήνας 5 έχει 31 ημέρες. Ο μήνας 6 έχει 30 ημέρες. Ο μήνας 7 έχει 31 ημέρες. Ο μήνας 8 έχει 31 ημέρες. Ο μήνας 9 έχει 30 ημέρες. Ο μήνας 10 έχει 31 ημέρες. Ο μήνας 11 έχει 30 ημέρες. Ο μήνας 12 έχει 31 ημέρες. Βλέπουμε στο παραπάνω παράδειγμα ότι ο αριθμός των στοιχείων της λίστας πρέπει να είναι ίδιος με το μέγεθος του πίνακα. Αν δώσουμε περισσότερα στοιχεία σ’ έναν πίνακα απ' ό,τι είναι το δηλωμένο μέγεθός του, τότε αυτό θεωρείται λάθος.

Καταχώρηση Τιμών σε Πίνακες Ακολουθεί ένα πρόγραμμα που καταχωρεί άρτιους αριθμούς σ’ έναν αυτόματο πίνακα : #include <stdio.h> #define SIZE 20

Page 44: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

main() { int counter, evens[SIZE]; for (counter=0; counter<SIZE; counter++) evens[counter] = 2 * counter; } Η C, όμως, δεν επιτρέπει την καταχώρηση τιμών από έναν πίνακα σ’ έναν άλλον, ούτε μπορούμε να χρησιμοποιήσουμε τη μορφή της λίστας στοιχείων μέσα σε αγκύλες, εκτός κι αν πρόκειται για απόδοση αρχικών τιμών. Ακολουθεί ένα πρόγραμμα που μας λέει τι δεν μπορούμε να κάνουμε με τους πίνακες στη C : #include <stdio.h> #define SIZE 5 main() { int pina[SIZE] = {5, 3, 2, 8}; /* όλα εντάξει */ int pinb[SIZE]; pinb = pina; /* δεν επιτρέπεται */ pinb[SIZE] = pina[SIZE]; /* ανεπίτρεπτο */ pinb[SIZE] = {5, 3, 2, 8}; /* δεν δουλεύει εδώ */ }

Δείκτες σε Πίνακες Είδαμε στα προηγούμενα ότι οι δείκτες (pointers) αποτελούν έναν συμβολικό τρόπο για να χρησιμοποιούμε διευθύνσεις μεταβλητών. Στην πραγματικότητα, ο συμβολισμός των πινάκων είναι μια μεταμφιεσμένη χρήση των δεικτών. Το όνομα ενός πίνακα είναι και ένας δείκτης, που δείχνει στο πρώτο στοιχείο του. Δηλαδή, αν pina είναι ένας πίνακας, τότε με : pina == &pina[0] παριστάνεται και η διεύθυνση μνήμης του πρώτου στοιχείου του πίνακα. Ακολουθεί ένα παράδειγμα που κάνει πράξεις με τις τιμές ενός δείκτη : /* prog42.c – πρόσθεση δείκτη */ #include <stdio.h> #define SIZE 4 main() { int pina[SIZE], *pti, index; float pinb[SIZE], *ptf; pti = pina; /* καταχωρεί τη διεύθυνση του πίνακα σε δείκτη */ ptf = pinb; for (index=0; index<SIZE; index++) printf("δείκτες + %d : %10u %10u\n", index, pti+index, ptf+index); } Το αποτέλεσμα θα είναι ως εξής : δείκτες + 0 : 56014 56026 δείκτες + 1 : 56016 56030 δείκτες + 2 : 56018 56034 δείκτες + 3 : 56020 56038

Page 45: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Στην πρώτη γραμμή τυπώνονται οι πρώτες διευθύνσεις των δύο πινάκων, όποιες είναι αυτές. Στην επόμενη γραμμή βλέπουμε το αποτέλεσμα της πρόσθεσης του 1 στη διεύθυνση. Για να δούμε τι γίνεται από κοντά : 56014 + 1 = 56016 ; 56026 + 1 = 56030 ; Ξέρουμε ότι ο τύπος int χρησιμοποιεί 2 bytes για αποθήκευση και ο τύπος float 4 bytes. Όταν λοιπόν προσθέτουμε 1 στον αντίστοιχο δείκτη, τότε η C προσθέτει μια μονάδα αποθήκευσης. Για τους πίνακες, όμως, αυτό σημαίνει ότι η διεύθυνση αυξάνεται στη διεύθυνση του επόμενου στοιχείου και όχι στο επόμενο byte. Προσθέτοντας έναν ακέραιο σ' έναν δείκτη ή αυξάνοντας έναν δείκτη, αλλάζει η τιμή του δείκτη κατά τον αριθμό των bytes του στοιχείου που δείχνει ο δείκτης. Ακολουθούν μερικές ενδιαφέρουσες εκφράσεις : dates + 2 == &dates[2] /* ίδια διεύθυνση */ *(dates + 2) == dates[2] /* ίδια τιμή */ Αυτές οι ισότητες δείχνουν τη στενή σχέση μεταξύ πινάκων και δεικτών. Αυτό σημαίνει ότι μπορούμε να χρησιμοποιήσουμε ένα δείκτη για να προσδιορίσουμε ένα συγκεκριμένο στοιχείο ενός πίνακα και να πάρουμε την τιμή του. Δεν πρέπει, όμως, να συγχέουμε την τιμή της *(dates+2) με την τιμή *dates+2, γιατί : *(dates + 2) /* είναι η τιμή του 3ου στοιχείου του πίνακα dates */ *dates + 2 /* το 2 προστίθεται στην τιμή του 1ου στοιχείου */ Μπορούμε συνεπώς να χρησιμοποιούμε και δείκτες για να δουλέψουμε με τους πίνακες. Ακολουθεί ένα πρόγραμμα που κάνει την ίδια δουλειά με το πρόγραμμα που είδαμε προηγουμένως, μόνο που τώρα χρησιμοποιούμε δείκτες αντί για πίνακες : /* prog43.c – χρησιμοποιεί δείκτες αντί για πίνακες */ #include <stdio.h> #define MONTHS 12 int days[MONTHS] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; main() { int index; for (index=0; index<MONTHS; index++) printf ("Ο μήνας %d έχει %d ημέρες.\n", index+1, *(days+index)); }

Συναρτήσεις, Πίνακες και Δείκτες Η C δεν επιτρέπει ολόκληροι πίνακες να είναι ορίσματα μιας συνάρτησης, αλλά για να κάνουμε τη δουλειά μας μπορούμε να περάσουμε (μεταβιβάσουμε) τη διεύθυνση ενός πίνακα σε μια συνάρτηση. Η συνάρτηση μπορεί μετά να χρησιμοποιήσει αυτήν την τιμή για τον χειρισμό του πρωτότυπου πίνακα. Είναι ένα κλασσικό παράδειγμα όπου πρέπει να χρησιμοποιήσουμε δείκτες για τον χειρισμό ενός πίνακα. Μπορούμε να χρησιμοποιήσουμε είτε συμβολισμό πίνακα είτε συμβολισμό δείκτη μέσα στη συνάρτηση. Ακολουθεί ένα παράδειγμα με μια συνάρτηση που επιστρέφει το άθροισμα των στοιχείων ενός πίνακα. /* prog44.c – άθροισμα στοιχείων ενός πίνακα */ #include <stdio.h> #define SIZE 10 float sum();

Page 46: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

main() { float pina[SIZE] = {20, 10, 5, 39, 4, 16, 19, 26, 31, 20}; float answer; answer = sum(pina, SIZE); printf("Το άθροισμα των στοιχείων είναι %d.\n", answer); } float sum(float * ar, int n) { int i; float s = 0; for (i=0; i<n; i++) { s += *ar; /* αθροιστής (s=s+περιεχόμενο της διεύθυνσης που δείχνει ο δείκτης ar) */ ar++; /*o δείκτης στο επόμενο στοιχείο*/ } return s; } Ο ar ξεκινάει δείχνοντας το πρώτο στοιχείο του πίνακα pina και με την πρόταση καταχώρησης total += *ar προστίθεται η τιμή του πρώτου στοιχείου, δηλ. η 20, στη μεταβλητή total. Μετά, η έκφραση ar++ αυξάνει τη μεταβλητή δείκτη ar έτσι ώστε αυτή να δείχνει στο επόμενο στοιχείο του πίνακα. Μπορούμε να γράψουμε τις προηγούμενες δύο εντολές και έτσι : total += *ar++; Δηλαδή, αυξάνεται ο δείκτης κατά ένα και όχι η τιμή που δείχνει και μάλιστα αυξάνεται μετά τη χρήση της τιμής του. Εάν χρησιμοποιήσουμε *++ar, τότε η σειρά θα ήταν : αύξηση του δείκτη και μετά χρήση της τιμής που δείχνει. Αλλά αν χρησιμοποιήσουμε την έκφραση (*ar)++, τότε το πρόγραμμα θα κάνει χρήση της τιμής που δείχνει η ar και μετά θα αυξήσει την τιμή αυτή κατά ένα και όχι τον δείκτη.

Οι Λειτουργίες των Δεικτών Η C έχει πέντε βασικές λειτουργίες δεικτών. Για να δούμε από κοντά τα αποτελέσματα της κάθε λειτουργίας, τυπώνουμε την τιμή του δείκτη, δηλ. τη διεύθυνση που δείχνει, την τιμή που είναι αποθηκευμένη στη διεύθυνση που δείχνει ο δείκτης και ακόμη και τη διεύθυνση του ίδιου του δείκτη. Ακολουθεί ένα παράδειγμα : /* prog45.c – λειτουργίες δεικτών */ #include <stdio.h> main() { static int pina[3] = {100, 200, 300}; int *ptr1, *ptr2; ptr1 = pina;/* καταχώρηση της διεύθυνσης του πίνακα σ' έναν δείκτη */ ptr2 = &pina[2]; /* καταχώρηση της διεύθυνσης του 3ου στοιχείου */ printf("ptr1 = %u, *ptr1 = %d, &ptr1 = %u\n", ptr1, *ptr1, &ptr1); ptr1++; /* αυξάνουμε τον δείκτη */ printf("ptr1 = %u, *ptr1 = %d, &ptr1 = %u\n", ptr1, *ptr1, &ptr1); printf("ptr2 = %u, *ptr2 = %d, &ptr2 = %u\n", ptr2, *ptr2, &ptr2); ++ptr2; /* περνάμε τα όρια του πίνακα */ printf("ptr2 = %u, *ptr2 = %d, &ptr2 = %u\n", ptr2, *ptr2, &ptr2);

Page 47: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

printf("ptr2 - ptr1 = %u\n", ptr2 - ptr1); /* τυπώνουμε τη διαφορά των δεικτών */ } Το αποτέλεσμα θα είναι ως εξής : ptr1 = 234, *ptr1 = 100, &ptr1 = 3606 ptr1 = 236, *ptr1 = 200, &ptr1 = 3606 ptr2 = 238, *ptr2 = 300, &ptr2 = 3604 ptr2 = 240, *ptr2 = 1910, &ptr2 = 3604 ptr2 – ptr1 = 2 Ας δούμε από κοντά τις πέντε βασικές λειτουργίες που μπορούμε να κάνουμε με τις μεταβλητές δείκτη : 1. Καταχώρηση. Μπορούμε να καταχωρήσουμε μια διεύθυνση σ' έναν δείκτη, είτε χρησιμοποιώντας ένα όνομα πίνακα ή χρησιμοποιώντας τον τελεστή διεύθυνσης & και την τιμή μιας μεταβλητής. 2. Εύρεση τιμής. Ο τελεστής * μάς δίνει την τιμή που βρίσκεται αποθηκευμένη στη θέση που δείχνεται από τον δείκτη. 3. Απόκτηση της διεύθυνσης ενός δείκτη. Και οι μεταβλητές δείκτη έχουν μια διεύθυνση και μια τιμή. 4. Αύξηση-μείωση ενός δείκτη. Με την αύξησή του ένας δείκτης μετακινείται στο επόμενο στοιχείο του πίνακα. Φυσικά, με ανάλογο τρόπο μπορούμε και να μειώσουμε έναν δείκτη. Η λειτουργία, όμως, ++ptr2 στο προηγούμενο παράδειγμα είχε σαν αποτέλεσμα ο ptr2 να μετακινηθεί κατά δύο bytes και να δείχνει ο,τιδήποτε μπορεί να αποθηκευθεί μετά τον πίνακα. Επίσης, πρέπει να θυμόμαστε ότι μπορούμε να χρησιμοποιούμε τους τελεστές αύξησης ή μείωσης με τις μεταβλητές δείκτη, αλλά όχι με σταθερές δείκτη. Δηλαδή, το ptr2 = urn++; δεν επιτρέπεται, όπως δεν επιτρέπεται και το 3++. 5. Διαφορά. Μπορούμε να βρούμε τη διαφορά μεταξύ δύο δεικτών.

Οι Πίνακες Πολλών Διαστάσεων Ένας μετεωρολόγος θέλει να αναλύσει τα δεδομένα της μηνιαίας βροχόπτωσης σε μια διάρκεια 5 ετών, δηλ. θα χρειαστεί συνολικά 5 Χ 12 = 60 μεταβλητές. Για να παραστήσει αυτά τα δεδομένα μπορεί να χρησιμοποιήσει 60 ανεξάρτητες μεταβλητές, μία για κάθε στοιχείο δεδομένων, κάτι φυσικά πολύ άβολο. Κάτι καλύτερο θα ήταν ένας πίνακας με 60 στοιχεία, αλλά ακόμα καλύτερο θα ήταν αν μπορούσε να κρατήσει ξεχωριστά τα 12 δεδομένα για τον κάθε χρόνο. Μπορεί δηλαδή να χρησιμοποιήσει 5 πίνακες, έναν για τα 12 στοιχεία του κάθε έτους, αλλά τι γίνεται αν τα έτη αυξηθούν και γίνουν 50; Μια καλή λύση είναι να χρησιμοποιήσουμε έναν πίνακα από πίνακες. Δηλ., ο κύριος πίνακας θα έχει 5 στοιχεία και το κάθε στοιχείο του θα είναι ένας πίνακας από 12 στοιχεία. Μιλάμε δηλαδή για πίνακα δύο διαστάσεων. Αυτό γίνεται ως εξής : static float rain[5][12]; Ο πίνακας rain είναι διδιάστατος και αποτελείται από 5 γραμμές και από 12 στήλες. Αν αλλάξουμε τον δεύτερο δείκτη, κινούμαστε σε μια γραμμή και αν αλλάξουμε τον πρώτο δείκτη, κινούμαστε κατά μήκος μιας στήλης. Για το συγκεκριμένο παράδειγμα, ο δεύτερος δείκτης δείχνει τους μήνες και ο πρώτος τα χρόνια. Ας δούμε κι ένα πρόγραμμα μ’ αυτόν τον διδιάστατο πίνακα. Ο στόχος του προγράμματος είναι να βρεθεί η συνολική βροχόπτωση για κάθε χρόνο, η μέση ετήσια βροχόπτωση και η μέση μηνιαία βροχόπτωση. Για να βρούμε τη συνολική βροχόπτωση του κάθε χρόνου, πρέπει να προσθέσουμε όλα τα δεδομένα του κάθε χρόνου, δηλ. της κάθε γραμμής. Για να βρούμε τη μέση βροχόπτωση για κάποιον μήνα, πρέπει να προσθέσουμε όλα τα δεδομένα σε μια δοσμένη στήλη.

Page 48: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Σταθερές Συμβολοσειράς Χαρακτήρα Όταν ο μεταγλωττιστής της C συναντά κάτι ανάμεσα σε διπλά εισαγωγικά " ", το θεωρεί σαν σταθερά συμβολοσειράς και οι χαρακτήρες μέσα στα εισαγωγικά αυτά μαζί με τον χαρακτήρα '\0' αποθηκεύονται σε γειτονικές θέσεις της μνήμης. Εάν, όμως, θέλουμε να χρησιμοποιήσουμε μέσα σε μια συμβολοσειρά διπλά εισαγωγικά, τότε πρέπει να βάλουμε πριν απ’ αυτά μια πλάγια κάθετο, ως εξής : printf("\"Τρέξε, Μαρία!\" είπε η Βάσω.\n"); Το αποτέλεσμα θα είναι το εξής : "Τρέξε, Μαρία!" είπε η Βάσω. Όλη η φράση μέσα στα εισαγωγικά δρα σαν δείκτης, εκεί που αποθηκεύεται η συμβολοσειρά, κάτι δηλ. ανάλογο με το όνομα ενός πίνακα, που δρα σαν ένας δείκτης στη θέση του πίνακα.

Πίνακες Συμβολοσειράς Χαρακτήρα Μπορούμε να δώσουμε αρχικές τιμές σε μια συμβολοσειρά χαρακτήρα ως εξής: char line[ ] = "Περιορίσου σε μια γραμμή"; Θα μπορούσαμε να κάνουμε το ίδιο και με τον γνωστό τρόπο απόδοσης αρχικών τιμών σ’ έναν πίνακα ως εξής : char line[ ] = {'Π', 'ε', 'ρ', 'ι', 'ο', 'ρ', 'ί', 'σ', 'ο', 'υ', ' ', 'σ', 'ε', ' ', 'μ', 'ι', 'α', ' ', 'γ', 'ρ', 'α', 'μ', 'μ', 'ή', '\0'}; H χρήση του μηδενικού χαρακτήρα '\0' εξηγεί ότι πρόκειται για συμβολοσειρά και όχι για πίνακα χαρακτήρων. Και στις δύο μορφές, ο μεταγλωττιστής μετράει τους χαρακτήρες και δίνει στον πίνακα το αντίστοιχο μέγεθος. Όπως συμβαίνει και με τους άλλους πίνακες, το όνομα του πίνακα line είναι ένας δείκτης στο πρώτο στοιχείο του πίνακα : line == &line[0] *line == 'Π' *(line+1) == line[1] == 'ε' Μπορούμε συνεπώς να χρησιμοποιήσουμε τον συμβολισμό δείκτη για να δημιουργήσουμε μια συμβολοσειρά : char *Mesolonghi = "\nMesolonghi per sempre"; που ισοδυναμεί με το : static char Mesolonghi[ ] = "\nMesolonghi per sempre";

Είσοδος Συμβολοσειράς Για το διάβασμα μιας συμβολοσειράς σ' ένα πρόγραμμα, πρέπει να κάνουμε δύο πράγματα : να δεσμεύσουμε τον χώρο αποθήκευσης γι αυτή τη συμβολοσειρά και να χρησιμοποιήσουμε μια συνάρτηση εισόδου για το διάβασμά της. Ο πιο απλός τρόπος για να δηλώσουμε με σαφήνεια το μέγεθος ενός πίνακα είναι η εξής δήλωση : char name[81]; Αφού έχει δημιουργηθεί χώρος στη μνήμη για τη συμβολοσειρά, μπορούμε πλέον να τη διαβάσουμε. Θα δούμε τις συναρτήσεις διαβάσματος συμβολοσειρών gets() και scanf().

Η Συνάρτηση gets() Η συνάρτηση αυτή διαβάζει χαρακτήρες μέχρι να συναντήσει τον χαρακτήρα enter '\n'. Η συνάρτηση αυτή δέχεται όλους τους χαρακτήρες πριν από τον χαρακτήρα '\n', προσθέτει τον μηδενικό χαρακτήρα '\0' και δίνει τη συμβολοσειρά στο καλούν πρόγραμμα. Ο χαρακτήρας νέας γραμμής διαβάζεται και αγνοείται. Ακολουθεί ένα απλό παράδειγμα χρήσης της συνάρτησης gets() : /* prog46.c – διάβασμα ενός ονόματος */ #include <stdio.h>

Page 49: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

#define MAX 81 main() { char name[MAX]; /* δέσμευση χώρου */ printf("Γεια σου, ποιο είναι το όνομά σου ;\n"); gets(name); printf("Γεια σου, %s.\n", name); } Αν όλα δουλέψουν σωστά, η gets() επιστρέφει τη διεύθυνση της συμβολοσειράς που διάβασε, αν όμως κάτι πάει στραβά ή αν η gets() συναντήσει EOF, επιστρέφει μια μηδενική διεύθυνση ή τίποτα. Η κενή διεύθυνση καλείται δείκτης κενού, παριστάνεται στο αρχείο <stdio.h> με τη σταθερά NULL και δεν έχει φυσικά καμία σχέση με τον χαρακτήρα του κενού διαστήματος. Έτσι, με τη gets() μπορούμε να ελέγχουμε για πιθανά λάθη ως εξής : while (gets(name) != NULL)

Η Συνάρτηση scanf() Η βασική διαφορά των δύο συναρτήσεων είναι στον τρόπο που αποφασίζουν ότι έχουν φθάσει στο τέλος της συμβολοσειράς. Μπορούμε να πούμε ότι η scanf() είναι περισσότερο μια συνάρτηση "απόκτησης λέξης" παρά "απόκτησης συμβολοσειράς". Η συνάρτηση scanf() έχει δύο επιλογές. Ξεκινά πάντα με τον πρώτο μη-λευκό χαρακτήρα που συναντάει. Αν χρησιμοποιούμε τον προσδιοριστή %s, η συμβολοσειρά φθάνει μέχρι τον επόμενο λευκό χαρακτήρα χωρίς να τον περιλαμβάνει. Αν καθορίσουμε ένα εύρος πεδίου, π.χ. %10s, τότε η scanf() λαμβάνει υπόψη της μέχρι 10 χαρακτήρες ή μέχρι τον πρώτο λευκό χαρακτήρα. Όπως είδαμε και στα προηγούμενα, η τιμής επιστροφής της scanf() είναι μια ακέραια τιμή, που είναι ίση με το αριθμό των στοιχείων που διαβάστηκαν με επιτυχία ή με τον χαρακτήρα EOF αν συναντήσει το τέλος αρχείου. Ακολουθεί ένα παράδειγμα : /* prog47. c – χρήση της scanf() */ #include <stdio.h> main() { static char name1[11], name2[11]; int count; printf("Παρακαλώ δώστε 2 ονόματα. \n"); count = scanf("%5s %10s", name1, name2); printf("Διάβασα τα %d ονόματα %s και %s. \n", count, name1, name2); } Το αποτέλεσμα θα είναι : Παρακαλώ δώστε 2 ονόματα. Τάκης Αντώνης Διάβασα τα 2 ονόματα Τάκης και Αντώνης. ή Παρακαλώ δώστε 2 ονόματα. Λένα Παπαδημητρίου Διάβασα τα 2 ονόματα Λένα και Παπαδημητρ. Αν έχουμε να δώσουμε μόνο κείμενο από το πληκτρολόγιο, τότε είναι καλύτερα να χρησιμοποιήσουμε τη συνάρτηση gets(), ενώ η scanf() συνιστάται για την είσοδο μικτών τύπων σε μια τυποποιημένη μορφή, όπως π.χ. αν κάθε γραμμή εισόδου περιέχει το όνομα ενός εργαλείου, τον κωδικό του αριθμού και την τιμή του.

Page 50: epl034 Limenes Askisis Paradeigmata · Ακόμη, η λακωνικότητα της c σε συνδυασμό με τον πλούτο των τελεστών που έχει, έχει

Έξοδος Συμβολοσειράς Θα δούμε δύο συναρτήσεις εξόδου, τις puts() και printf().

Η Συνάρτηση puts() Η συνάρτηση puts() είναι αρκετά εύκολη στη χρήση και απλά δίνουμε ένα όρισμα που είναι ένας δείκτης σε μια συμβολοσειρά. Ακολουθεί ένα παράδειγμα : /* prog48.c – χρήση της puts */ #include <stdio.h> #define DEF "Είμαι μια συμβολοσειρά που έχει ορισθεί." main() { static char str1[ ] = "Ένας πίνακας μού απέδωσε αρχικές τιμές."; char *str2 = "Ένας δείκτης μού απέδωσε αρχικές τιμές."; puts("Είμαι ένα όρισμα στην puts()."); puts(DEF); puts(str1); puts(str2); puts(&str1[4]); puts(str2+4); } Το αποτέλεσμα θα είναι : Είμαι ένα όρισμα στην puts(). Είμαι μια συμβολοσειρά που έχει ορισθεί. Ένας πίνακας μού απέδωσε αρχικές τιμές. Ένας δείκτης μού απέδωσε αρχικές τιμές. πίνακας μού απέδωσε αρχικές τιμές. δείκτης μού απέδωσε αρχικές τιμές. Προσέχουμε στα δύο τελευταία παραδείγματα από πού αρχίζει να τυπώνει η puts. Κάθε συμβολοσειρά με την puts() εμφανίζεται σε μια νέα γραμμή.

Η Συνάρτηση printf() Όπως ήδη γνωρίζουμε, η printf() δεν τοποθετεί αυτόματα κάθε συμβολοσειρά σε μια νέα γραμμή, αλλά εμείς πρέπει να δείξουμε πού θέλουμε να ξεκινούν οι νέες γραμμές. Γενικά, με την printf() πρέπει να γράψουμε περισσότερα, αλλά μ’ αυτήν μπορούμε να συνδυάσουμε σε μια γραμμή πολλές συμβολοσειρές.