1η+2η εβδομάδα 24 Μαρτίου 2014 1 Ιστορία της C Η γλώσσα C επινοήθηκε στα εργαστήρια Bell της AT&T την περίοδο από το 1969 έως το 1973 κατά κύριο λόγο από τον Dennis Ritchie. Εκείνη τη χρονική περίοδο υπήρχαν ήδη γλώσσες προγραμματισμού με τις οποίες θα μπορούσε κανείς να προγραμματίσει εφαρμογές αλλά δεν υπήρχαν γλώσσες με δυνατότητες χαμηλού επιπέδου με τις οποίες θα μπορούσε κάποιος να προγραμματίσει ένα λειτουργικό σύστημα ή ένα μεταγλωττιστή. Έτσι οι προγραμματιστές λειτουργικών συστημάτων έπρεπε να χρησιμοποιούν κώδικα μηχανής για να προγραμματίσουν λειτουργικά συστήματα. Την ίδια περίοδο, ένας συνάδελφος του Dennis Ritchie, ο Ken Thompson χρησιμοποιούσε τη γλώσσα B για αυτό το σκοπό. Όταν στα Bell Labs παρέλαβαν έναν νέο υπολογιστή τον PDP-11 είδαν ότι η γλώσσα B δεν είχε τις δυνατότητες που έπρεπε να έχει για να φτιαχτεί με αυτήν ένα λειτουργικό σύστημα για το νέο υπολογιστή. Έτσι ο Ritchie ασχολήθηκε με την ανάπτυξη της διαδόχου της B, η οποία ονομάστηκε C. To 1978 o Ritchie με τον Brian Kernighan έγραψαν ένα βιβλίο στο οποίο περιγράφουν τη νέα γλώσσα. Το βιβλίο θεωρείται κλασσικό και αποτέλεσε το ανεπίσημο πρότυπο της γλώσσας. Το συντακτικό της C όπως περιγράφεται εκεί αναφέρεται ως K&R. Κάποια χρόνια μετά, όταν η γλώσσα έγινε πολύ δημοφιλής και έπρεπε να προτυποποιηθεί το Αμερικανικό Εθνικό Ινστιτούτο Προτύπων (ANSI) εξέδωσε το 1989 ένα πρώτο πρότυπο της C το οποίο αναφέρεται και ως C89 ή ANSI C. Ένα χρόνο μετά ο Διεθνής Οργανισμός Προτυποποίησης (ISO) εξέδωσε το ίδιο στην ουσία πρότυπο το οποίο αναφέρεται ως C90 αλλά στην ουσία είναι ίδιο με το C89. Το 1999 εκδόθηκε ένα ακόμα πρότυπο της γλώσσας το οποίο αναφέρεται ως C99 και με αυτήν την έκδοση της γλώσσας θα ασχοληθούμε στο μάθημα¹. Περισσότερες πληροφορίες (ιστορικές και μη) για τη C μπορεί κανείς να βρει στη wikipedia². Το βιβλίο των Kernighan-Ritchie θεωρείται κλασσικό και αξίζει κανείς να το διαβάσει παρόλο που είναι μάλλον ακατάλληλο για διδασκαλία σε αρχάριους. Μπορείτε να βρείτε στο δίκτυο διάφορες πηγές όπως το βιβλίο The C Book³ ή tutorials και βοηθήματα όπως αυτά εδώ⁴, εδώ⁵ και εδώ⁶. Σημειώσεις, ¹Στα τέλη του 2011 εκδόθηκε το C11 το οποίο είναι πολύ φρέσκο. Η συμβατότητα των μεταγλωττιστών με αυτό θα είναι μικρή για αρκετό καιρό ακόμα οπότε δε θα ασχοληθούμε ιδιαίτερα με αυτό. ²http://en.wikipedia.org/wiki/C_programming_language ³http://publications.gbdirect.co.uk/c_book/ ⁴http://www.cprogramming.com/tutorial/c-tutorial.html ⁵http://www.mycplus.com/category/tutorials/c-programming-tutorials/ ⁶http://www.csd.uwo.ca/ jamie/c/index.html 1
εργασίες και όποιο άλλο υλικό του μαθήματος σας δίνεται από τον διδάσκοντα θα αναρτάται εδώ⁷. 2 Ένα απλό πρόγραμμα σε C Το πρόγραμμα 1 τυπώνει στην οθόνη το μήνυμα Hello, world!. 1 # include <stdio.h> Listing 1: Πρόγραμμα Hello 2 3 int main() 4 { 5 printf("hello, world!"); 6 7 return 0; 8 } Η γραμμή 1 δεν είναι κάποια εντολή της C αλλά μία εντολή του προεπεξεργαστή της C, δηλαδή ενός προγράμματος που τρέχει πριν από το μεταγλωττιστή και πραγματοποιεί κάποιες μετατροπές στο πρόγραμμα πριν γίνει η μεταγλώττιση. Η συγκεκριμένη εντολή λέει στον προεπεξεργαστή να βρει το αρχείο stdio.h και να βάλει τα περιεχόμενά του στη γραμμή 1 του παραπάνω προγράμματος. Τα αρχεία με κατάληξη.h συνήθως δεν περιέχουν εντολές της C αλλά δηλώσεις συναρτήσεων που βρίσκονται μεταγλωττισμένες ήδη και πακεταρισμένες σε βιβλιοθήκες του συστήματος προκειμένου να διευκολύνουν τον προγραμματιστή. Τα αρχεία αυτά ονομάζονται αρχεία συμπερίληψης (include files) και παρέχονται από τον προμηθευτή του μεταγλωττιστή. Το συγκεκριμένο αρχείο stdio.h περιέχει δηλώσεις συναρτήσεων της βιβλιοθήκης εισόδου/εξόδου και το όνομά του προκύπτει ως συντόμευση των λέξεων standard input/output. Η συνάρτηση printf της γραμμής 5 είναι μία από αυτές τις συναρτήσεις της στάνταρ βιβλιοθήκης και υλοποιεί την εμφάνιση μηνυμάτων και τιμών στην οθόνη. Το όνομα της προκύπτει από τις λέξεις print function (συνάρτηση εκτύπωσης). Για να καλέσουμε μια συνάρτηση στη C γράφουμε το όνομά της ακολουθούμενο από τα πραγματικά της ορίσματα. Στη συγκεκριμένη περίπτωση το μοναδικό όρισμα της printf είναι μία συμβολοσειρά (string) που περιέχει το μήνυμα που θέλουμε να τυπωθεί στην οθόνη. Στην C οι συμβολοσειρές περικλείονται μέσα σε διπλά εισαγωγικά. Οτιδήποτε βρίσκεται μέσα στα διπλά εισαγωγικά τυπώνεται στην οθόνη ως έχει χωρίς η γλώσσα να το υποβάλει σε κάποια ιδιαίτερη επεξεργασία⁸. Παρατηρήστε επίσης ότι η εντολή της γραμμής 5 τερματίζεται με το ελληνικό ερωτηματικό. Κάθε εντολής της C τερματίζεται με το ελληνικό ερωτηματικό, όπως και η εντολή return 0 της γραμμής 7. Η γραμμή 3 είναι η επικεφαλίδα (header) μίας συνάρτησης (function) με όνομα main. Θα δούμε αναλυτικά τον τρόπο ορισμού συναρτήσεων στην C αργότερα. Προς το παρόν αρκεί να πούμε ότι κάθε εκτελέσιμη εντολή σε ένα πρόγραμμα C πρέπει να βρίσκεται μέσα σε μία συνάρτηση. Μπορούμε να δώσουμε οποιοδήποτε όνομα στις συναρτήσεις που ορίζουμε (αρκεί να ακολουθούν τους κανόνες της γραμματικής της C) αλλά κάθε πρόγραμμα πρέπει να περιέχει μία συνάρτηση η οποία ονομάζεται main. Η εκτέλεση ενός προγράμματος C συνίσταται στην εκτέλεση αυτής της συνάρτησης, ή με άλλα ⁷ ⁸Με εξαίρεση τους χαρακτήρες % και \ για τους οποίους θα μιλήσουμε στη συνέχεια 2
λόγια η εκτέλεση ενός προγράμματος C ξεκινάει με την πρώτη εντολή της συνάρτησης main και συνεχίζεται με τις επόμενες μέχρι την τελευταία της εντολή. Στο συγκεκριμένο μας πρόγραμμα ο ορισμός της main δηλαδή η επικεφαλίδα της μαζί με τις εντολές που εκτελούνται όταν καλείται βρίσκονται στις γραμμές 3 έως 8. Οι γραμμές 4 έως 8 είναι το σώμα (body) της συνάρτησης και πρέπει να περικλείονται μέσα σε ένα ζευγάρι άγκιστρων {}. Στη γραμμή 3 επίσης βλέπουμε ένα ζευγάρι παρενθέσεων αμέσως μετά το όνομα της συνάρτησης. Εκεί μπορούμε να δηλώσουμε τις τυπικές παραμέτρους της συνάρτησης. Στο παράδειγμά μας ορίζουμε τη main χωρίς παραμέτρους αλλά θα δούμε στη συνέχεια ότι έχουμε άλλη μία επιλογή. Επίσης βλέπουμε τη λέξη int πριν από το όνομα της συνάρτησης. Αυτό σημαίνει ότι η συνάρτηση επιστρέφει έναν ακέραιο. Στη C όλες οι συναρτήσεις πρέπει να δηλώνουν τι επιστρέφουν, ακόμα και όταν δεν επιστρέφουν κάποια τιμή. Οι συναρτήσεις μπορούν να επιστρέφουν τιμές στις καλούσες με την εντολή return. Στη γραμμή 7 βλέπουμε ότι στο πρόγραμμά μας η main επιστρέφει την τιμή 0. 3 Βασικοί τύποι και προσδιοριστές Στη C έχουμε τέσσερις βασικούς τύπους: int Για ακεραίους, float για αριθμούς κινητής υποδιαστολής, double για αριθμούς κινητής υποδιαστολής διπλής ακρίβειας και char για χαρακτήρες⁹. Το εύρος κάθε τύπου, δηλαδή το σύνολο τιμών των μεταβλητών αυτού του τύπου καθορίζεται μόνο εν μέρει από το πρότυπο της C. Το πρότυπο δίνει την ελευθερία σε αυτόν που αναπτύσσει το μεταγλωττιστή να ορίσει το εύρος τιμών για κάθε έναν από τους παραπάνω τύπους και για κάθε συγκεκριμένη αρχιτεκτονική υπολογιστή αρκεί να κινείται μέσα σε κάποια όρια. Για παράδειγμα το πρότυπο ορίζει ότι ένας ακέραιος θα πρέπει να παριστάνει αριθμούς τουλάχιστον στο εύρος από -32768 μέχρι και 32767. Αυτό δεν σημαίνει ότι σε κάθε μεταγλωττιστή θα ισχύει κάτι τέτοιο καθώς κάποιοι μεταγλωττιστές μπορεί να ορίζουν τον ακέραιο τύπο να παριστάνει μεγαλύτερο εύρος τιμών. Στην περίπτωση που ο προγραμματιστής δεν καλύπτεται από το εύρος τιμών των ακεραίων σε κάποιο συγκεκριμένο μεταγλωττιστή δίνονται κάποιες δυνατότητες περαιτέρω προσδιορισμού ενός τύπου με τους προσδιοριστές (specifiers) long και short. Συγκεκριμένα ο τύπος long int δηλώνει έναν ακέραιο μεγαλύτερου ή ίσου εύρους από εκείνο του int και ο short int δηλώνει έναν ακέραιο με εύρος μικρότερο ή ίσο από εκείνο του int. Επιπρόσθετα, δίνεται η δυνατότητα χρησιμοποίησης του προσδιοριστή long δύο φορές δηλαδή ορίζεται και ο τύπος long long int προκειμένου να δηλωθεί ένας ακέραιος με εύρος μεγαλύτερο ή ίσο από εκείνο του long int. Ο προσδιοριστής short δεν μπορεί να χρησιμοποιηθεί δύο φορές. Επίσης, όταν χρησιμοποιείται κάποιος από τους δύο παραπάνω προσδιοριστές short ή long η λέξη int μπορεί να παραληφθεί. Δηλαδή ο τύπος ⁹Το πρότυπο C99 ορίζει και δύο ακόμα τύπους τους _Bool και _Complex με τους οποίους δε θα ασχοληθούμε καθώς δεν αναφέρονται πουθενά στην πιστοποίηση 3
long long int μπορεί να γραφεί και ως long long όπως και ο τύπος short int μπορεί να γραφτεί και ως short. Οι προσδιοριστές short και long μπορούν να χρησιμοποιηθούν μόνο σε τύπους int με μία και μόνη εξαίρεση: ο long μπορεί να χρησιμοποιηθεί και με τον τύπο double. Επίσης, ένα άλλο είδος προσδιοριστών μπορεί να καθορίσει στην περίπτωση βαθμωτών τύπων (int και char) αν θέλουμε ο τύπος να θεωρείται προσημασμένος ή όχι. Οι προσδιοριστές αυτοί είναι οι signed και unsigned οι οποίοι μπαίνουν πριν από το όνομα του βασικού τύπου (και πριν από τους προσδιοριστές long ή short αν αυτοί υπάρχουν). Έτσι, επιπρόσθετα στους παραπάνω συνδυασμούς τύπων και προσδιοριστών μπορούμε να δηλώσουμε μία μεταβλητή ως τύπου signed int ή unsigned short int ή signed char. Ξανατονίζουμε ότι οι προσδιοριστές αυτοί δεν μπορούν να χρησιμοποιηθούν με τύπους κινητής υποδιαστολής δηλαδή float και double. Ο λόγος ύπαρξης αυτών των προσδιοριστών είναι το γεγονός ότι η αναπαράσταση του προσήμου σε ένα βαθμωτό τύπο απαιτεί τη δέσμευση ενός bit. Καταργώντας τη χρησιμοποίηση του bit αυτού για την αναπαράσταση του προσήμου μπορούμε να αναπαραστήσουμε αριθμούς διπλάσιου εύρους. Ένα τελευταίο σημείο είναι ότι σε περίπτωση που ο προσδιοριστής προσήμου δεν αναφέρεται, τότε ο τύπος θεωρείται προσημασμένος. Δηλαδή ο τύπος long int θεωρείται ισοδύναμος με τον προσημασμένο signed long int. Συνοψίζοντας τα παραπάνω, ο πίνακας 3 δείχνει ποιοι προσδιοριστές επιτρέπονται για ποιους τύπους. Παρατηρήστε ότι στον τύπο int μπορούν να χρησιμοποιηθούν όλοι οι προσδιοριστές ενώ στον float κανένας. Στον double εφαρμόζεται μόνο ο long ενώ στον char μόνο οι signed/unsigned. Πίνακας 1: Επιτρεπόμενοι προσδιοριστές ανά τύπο Τύπος short long long long signed/unsigned int OK OK OK OK char OK float double OK Όπως προαναφέρθηκε, το πρότυπο της C δεν ορίζει ακριβώς το εύρος τιμών των παραπάνω τύπων. Ορίζει τις μεταξύ τους (προφανείς) σχέσεις δηλαδή ότι ένας long int πρέπει να είναι μεγαλύτερου ή ίσου εύρους με έναν int και κάποια ελάχιστα όρια όπως π.χ. ότι ένας int δεν μπορεί να είναι μικρότερος από δύο bytes αλλά τίποτα πιο συγκεκριμένο. Το ακριβές εύρος κάθε τύπο εξαρτάται από το μεταγλωττιστή που χρησιμοποιεί κανείς. Μπορεί δηλαδή στον ίδιο υπολογιστή, δύο διαφορετικοί μεταγλωττιστές να ορίζουν διαφορετικά όρια για τον ίδιο τύπο. Είναι όμως υποχρεωτικό για κάθε μεταγλωττιστή να δίνει στον προγραμματιστή τη δυνατότητα να διαπιστώσει τη στιγμή που τρέχει το πρόγραμμα το εύρος τιμών ενός οποιοδήποτε τύπου. Τις σχετικές λεπτομέρειες μπορείτε να τις δείτε στο τέλος του κεφαλαίου. 4
4 Δηλώσεις μεταβλητών και απόδοση τιμής Για να δηλώσουμε μια μεταβλητή πρέπει να αναφέρουμε το όνομα του τύπου της ακολουθούμενο από το όνομά της και φυσικά το ελληνικό ερωτηματικό, π.χ. κάπως έτσι: int a; ή long double b;. Για παράδειγμα στο listing 2 δηλώνεται μία μεταβλητή a στην οποία ανατίθεται η τιμή 2 και στη συνέχεια η τιμή της a τυπώνεται στην οθόνη. 1 # include <stdio.h> Listing 2: Δήλωση μεταβλητής 2 3 int main() 4 { 5 int a; 6 7 a = 2; 8 printf("%d\n", a); 9 10 return 0; 11 } Η δήλωση της μεταβλητής γίνεται στη γραμμή 5. Στη γραμμή 7 γίνεται η ανάθεση της τιμής 2 στη μεταβλητή και στην επόμενη γραμμή τυπώνεται στην οθόνη. Στη συνέχεια θα πούμε περισσότερες λεπτομέρειες για τα ιερογλυφικά που εμφανίζονται στη γραμμή 8 μέσα στα διπλά εισαγωγικά. Προς το παρόν θα πρέπει απλώς να μάθετε τη γραμμή 8 απ έξω και να βάζετε αντί για το όνομα της μεταβλητής a το όνομα της μεταβλητής που θέλετε να εμφανιστεί στην οθόνη. Επίσης, αντί για %d που χρησιμοποιείται για την εκτύπωση ακεραίων θα πρέπει να χρησιμοποιείτε %f για την εκτύπωση αριθμών κινητής υποδιαστολής. Ένα ζήτημα είναι το σημείο στο οποίο επιτρέπεται να δηλώνονται μεταβλητές. Το παλιότερο πρότυπο της γλώσσας (C89, C90) ορίζει ότι όλες οι δηλώσεις τοπικών¹⁰ μεταβλητών πρέπει να βρίσκονται αμέσως μετά το άγκιστρο που ξεκινάει το σώμα μίας συνάρτησης και πριν από οποιαδήποτε εκτελέσιμη εντολή. Στο παραπάνω παράδειγμα δε θα μπορούσαμε να δηλώσουμε μία μεταβλητή π.χ. στη γραμμή 9 γιατί αυτή βρίσκεται μετά την εκτελέσιμη εντολή της γραμμής 8. Με άλλα λόγια πρέπει όλες οι δηλώσεις μεταβλητών να βρίσκονται μαζεμένες αμέσως μετά το άγκιστρο της γραμμής 4 για την περίπτωση της main. Παρόλα αυτά, είναι σύνηθες ο μεταγλωττιστής να μην γκρινιάζει αν βάλετε μία δήλωση σε κάποιο ξεκάρφωτο σημείο. Αυτό μπορεί να οφείλεται αφενός στο ότι το νεότερο πρότυπο C99 επιτρέπει τις δηλώσεις να ανακατεύονται με τον κώδικα ή αφετέρου στο ότι η C++ ανέκαθεν επέτρεπε κάτι τέτοιο οπότε πολλοί μεταγλωττιστές το επέτρεπαν σαν ευκολία. Εσείς καλύτερα να κάνετε αυτό που λένε οι κανόνες και που κατά πάσα πιθανότητα θα ισχύει σε κάθε μεταγλωττιστή της C και το οποίο θα αντιλαμβάνεται οποιοσδήποτε διορθωτής στην πιστοποίηση: Βάλτε όλες τις δηλώσεις μεταβλητών μαζεμένες αμέσως μετά το άγκιστρο της συνάρτησης μέσα στην οποία δηλώνονται και πριν από οποιαδήποτε εκτελέσιμη εντολή. Επιτρέπεται στην ίδια γραμμή να ορίζονται περισσότερες από μία μεταβλητές εφόσον έχουν τον ίδιο τύπο. Για παράδειγμα στο παρακάτω πρόγραμμα 3, στη γραμμή 5 δηλώνονται τρεις μεταβλητές ¹⁰Υπάρχουν και οι καθολικές (global) μεταβλητές οι οποίες δηλώνονται έξω από το σώμα οποιασδήποτε συνάρτησης. 5
με τα ονόματά τους χωρισμένα με κόμματα. Επίσης δίνεται η δυνατότητα απόδοσης τιμής στην ίδια τη δήλωση. Στη γραμμή 6 η μεταβλητή d παίρνει την τιμή 47 και η μεταβλητή e την τιμή 82. 1 # include <stdio.h> Listing 3: Κι άλλες δηλώσεις 2 3 int main() 4 { 5 int a, b, c; 6 int d = 47, e = 82; 7 8 return 0; 9 } Μία τελευταία παρατήρηση για τις αριθμητικές σταθερές που μπορούμε να αποθηκεύσουμε σε ακέραιες μεταβλητές. Οι αριθμοί 47 και 82 του προηγούμενου παραδείγματος είναι ακέραιοι αριθμοί στο δεκαδικό σύστημα. Η C μας δίνει τη δυνατότητα να γράψουμε ακέραιους αριθμούς με βάση το 16, το 8 ή το 2 γράφοντας μπροστά τους το πρόθεμα 0x, 0 ή 0b αντίστοιχα. Τρέξτε το παρακάτω πρόγραμμα για να το επιβεβαιώσετε. 1 # include <stdio.h> 2 int main() 3 { 4 int a; Listing 4: Ακέραιες σταθερές 5 6 a = 24; /* Dekadiko 24 */ 7 printf("h timi tns a eivai: %d\n", a); 8 a = 0x24; /* Dekae3adiko 24 -> 36 */ 9 printf("h timi tns a eivai: %d\n", a); 10 a = 024; /* Oktadiko 24 -> 20 */ 11 printf("h timi tns a eivai: %d\n", a); 12 a = 0b100; /* Dyadiko 100 -> 4 */ 13 printf("h timi tns a eivai: %d\n", a); 14 15 return 0; 16 } 5 Εισαγωγικά για τη φορμαρισμένη είσοδο και έξοδο Θα ασχοληθούμε με τη φορμαρισμένη είσοδο και έξοδο αναλυτικά σε επόμενη ενότητα αλλά είναι χρήσιμο σε πρώτη φάση να δούμε πώς μπορούμε στη C να τυπώνουμε μεταβλητές και σταθερές στην οθόνη όπως και να τις διαβάζουμε από το πληκτρολόγιο. Το απλό παράδειγμα 5 δηλώνει μία ακέραια μεταβλητή a και την τυπώνει με τη συνάρτηση printf. 6
1 # include <stdio.h> Listing 5: printf 2 3 int main() 4 { 5 int a; 6 7 a = 2; 8 printf("h timn tns a eivai: %d\n", a); 9 10 a = -1; 11 printf("h timn tns a eivai: %d\n", a); 12 13 return 0; 14 } Αν μεταγλωττίσετε το παραπάνω πρόγραμμα και το τρέξετε θα δείτε στην οθόνη τα παρακάτω: H timn tns a eivai: 2 H timn tns a eivai: -1 Ας δούμε αναλυτικά πώς δουλεύει η printf. Δυστυχώς δεν υπάρχει δυνατότητα να γράψουμε μία εντολή της μορφής printf(a); όπως θα κάναμε σε άλλες γλώσσες για να τυπώσουμε την τιμή της a στην οθόνη¹¹. Το πρώτο όρισμα της printf πρέπει πάντα να είναι μία συμβολοσειρά δηλάδη ένα σύνολο χαρακτήρων κλεισμένων μέσα σε διπλά εισαγωγικά. Όπως είδαμε και στο πρόγραμμα Hello, world! η C θα τυπώσει στην οθόνη έναν προς έναν τους χαρακτήρες της συμβολοσειράς. Η εντολή printf("hello, world!"); θα τυπώσει στην οθόνη ακριβώς το μήνυμα Hello, world! Στο πρόγραμμα 5 όμως βλέπουμε ότι η συμβολοσειρά H timn tns a eivai: %d\n δεν τυπώνεται ακριβώς έτσι. Για την ακρίβεια δεν βλέπουμε ούτε το %d ούτε το \n. Το πρώτο είναι ένας προσδιοριστής (specifier) και το δεύτερο ένας χαρακτήρας διαφυγής (escape character). Ο κανόνας είναι ότι η printf εκτός από το πρώτο όρισμα που είναι μία συμβολοσειρά και είναι υποχρεωτικό, μπορεί να δεχτεί και οσαδήποτε ακόμα ορίσματα χωρισμένα με κόμματα το ένα από το άλλο των οποίων θα τυπώσει τις τιμές. Αυτό όμως από μόνο του δεν αρκεί. Η printf θέλει να δηλώσουμε ρητά τον τρόπο με τον οποίο θέλουμε να τυπωθεί η κάθε παράσταση. Για παράδειγμα μπορεί να μη θέλουμε απλώς να τυπώσουμε μία τιμή αλλά να τη στοιχίσουμε μαζί με άλλες οπότε θα πρέπει να δηλώσουμε πόσες θέσεις θα καταλαμβάνει. Ή μπορεί να θέλουμε να τυπωθεί στοιχισμένη δεξιά ή αριστερά, να θέλουμε ο κενός χώρος όταν γίνεται στοίχιση να γεμίζει με κενά ή με μηδενικά. Στην περίπτωση των αριθμών κινητής υποδιαστολής μπορεί να έχουμε όλα τα προηγούμενα αλλά επίσης να θέλουμε να γίνεται εκτύπωση με συγκεκριμένο αριθμό δεκαδικών ψηφίων όπως επίσης μπορεί να θέλουμε εκτύπωση σε απλή ή επιστημονική γραφή. Οπότε για κάθε παράσταση που ακολουθεί την αρχική συμβολοσειρά και θέλουμε να τυπωθεί, πρέπει να συμπεριλάβουμε τον αντίστοιχο προσδιοριστή μέσα στη συμβολοσειρά. Οι προσδιοριστές ξεκινούν πάντα με το χαρακτήρα %. Πρέπει να ¹¹αν και μπορούμε να γράψουμε μία δική μας συνάρτηση για αυτό το σκοπό 7
υπάρχουν τόσοι προσδιοριστές όσες και παραστάσεις μετά τη συμβολοσειρά δηλαδή τα % μέσα στη συμβολοσειρά πρέπει να είναι τόσα όσες και οι παραστάσεις που την ακολουθούν. Ο κάθε προσδιοριστής αντιστοιχίζεται σε μία παράσταση όπως τους διαβάζουμε από τα αριστερά προς τα δεξιά. Δηλαδή στην εντολή printf("mpla %d mplou %u mpli %x", a, b, c); ο προσδιοριστής %d αντιστοιχεί στη μεταβλητή a, ο προσδιοριστής %u στη μεταβλητή b και ο %x στη c. Τελικά, όταν η printf εκτελείται ξεκινά να τυπώνει έναν προς έναν τους χαρακτήρες της συμβολοσειράς που αποτελεί το πρώτο της όρισμα από τα αριστερά προς τα δεξιά. Αν συναντήσει έναν προσδιοριστή, τον επεξεργάζεται και ανάλογα τυπώνει την αντίστοιχη παράσταση (χωρίς να τυπώσει τον προσδιοριστή). Συνεχίζει με τον ίδιο τρόπο μέχρι το τέλος της συμβολοσειράς. Στο τελευταίο παράδειγμα δηλαδή η printf θα τύπωνε τους χαρακτήρες mpla και ένα κενό. Μετά θα συναντούσε τον προσδιοριστή %d οπότε θα τύπωνε την τιμή της μεταβλητής a¹². Μετά θα τύπωνε ένα κενό και τους χαρακτήρες mplou και ένα κενό. Έπειτα θα έβρισκε τον προσδιοριστή %u οπότε θα τύπωνε την τιμή της αντίστοιχης παράστασης δηλαδή της μεταβλητής b κ.ο.κ. Με ανάλογο τρόπο μπορεί κανείς να χρησιμοποιήσει τη συνάρτηση scanf για να διαβάσει τιμές μεταβλητών από το πληκτρολόγιο. Με την παρακάτω εντολή διαβάζεται μία προσημασμένη ακέραια τιμή από το πληκτρολόγιο και αποθηκεύεται στη μεταβλητή a: scanf("%d", &a);. Προσοχή στο & που βρίσκεται πριν από το όνομα της μεταβλητής. Στην ενότητα 5.1 που ακολουθεί περιγράφονται σχετικά πιο αναλυτικά οι προσδιοριστές χωρίς να περιγράφονται πλήρως. Ακόμα και σε αυτήν την απλουστευμένη παρουσίαση είναι αρκετά πολύπλοκοι. Δεν χρειάζεται να τους διαβάσετε και να τους κατανοήσετε πλήρως σε πρώτη ανάγνωση. Απλώς κρατήστε τους σαν αναφορά. Αυτό που χρειάζεται να ξέρετε σε πρώτη φάση είναι ότι μπορείτε να χρησιμοποιείτε τον προσδιοριστή %d για ακέραιους, τον %u για μη-προσημασμένους ακέραιους και τον %c για χαρακτήρες. 5.1 Προσδιοριστές Η σύνταξη των προσδιοριστών είναι αρκετά πολύπλοκη και περιγράφεται αναλυτικά στην ενότητα 7.9.16.1 του προτύπου C99. Για λόγους πληρότητας και μόνο τους αναφέρουμε εδώ αλλά πρέπει να τονίσουμε ότι δεν είναι απαραίτητο να γνωρίζει κανείς όλα τα παρακάτω για να ξεκινήσει να μαθαίνει την C. Η μορφή ενός προσδιοριστή είναι: Μία ή περισσότερες σημαίες (flags) (προαιρετικά), ένα ελάχιστο μήκος πεδίου (προαιρετικά), η ακρίβεια (προαιρετικά), ένας τροποποιητής μεγέθους (προαιρετικά αλλά χρησιμοποιείται συχνά) και ένας μετατροπέας. Από τα παραπάνω το πιο σημαντικό είναι το τελευταίο δηλαδή ο μετατροπέας. Αυτός καθορίζει ως τι θα χειριστεί η printf την παράσταση που της ζητείται να τυπώσει. Το παρακάτω παράδειγμα (listing 6) βοηθάει να γίνει αυτό πιο κατανοητό: ¹²σύμφωνα με τον τρόπο που ορίζει ο προσδιοριστής %d, θα τους δούμε αναλυτικά στη συνέχεια 8
1 # include <stdio.h> Listing 6: Μυστηριώδης printf 2 3 int main() 4 { 5 int a = -1; 6 7 printf("h timn tns a eivai: %d\n", a); 8 printf("h timn tns a eivai: %u\n", a); 9 10 return 0; 11 } Αν τρέξετε το παραπάνω σε έναν υπολογιστή που αποθηκεύει ακεραίους με τη μορφή συμπληρώματος ως προς 2 θα δείτε στην οθόνη τα εξής: H timn tns a eivai: -1 H timn tns a eivai: 4294967295 Τελικά, ποια είναι η τιμή της μεταβλητής a; Γιατί η πρώτη printf τυπώνει -1 ενώ η δεύτερη 4294967295; Κατά μία έννοια η μεταβλητή a είναι και τα δύο. Από φυσική άποψη, από την άποψη του υπολογιστή στον οποίο έτρεξε το παραπάνω παράδειγμα δηλαδή, η μεταβλητή a είναι 4 συνεχόμενα bytes δηλαδή 32 bits τα οποία έχουν όλα την τιμή 1. Αν αυτά τα 32 bits τα θεωρήσει κανείς ένα προσημασμένο ακέραιο αριθμό τότε στη μορφή συμπληρώματος ως προς 2 αυτός ο αριθμός θα ήταν ο -1. Αν τα θεωρήσει κανείς ένα μη προσημασμένο αριθμό τότε είναι ο αριθμός 2 32 1 = 4294967295. Οπότε τελικά ο μετατροπέας που ακολουθεί το % στην printf καθορίζει ως τι πρέπει η συνάρτηση να τυπώσει την παράσταση που ακολουθεί. Ο μετατροπέας d στη γραμμή 7 λέει στην printf να τυπώσει την a ως προσημασμένο ακέραιο (-1) ενώ ο u στη γραμμή 8 ως μη προσημασμένο ακέραιο (4294967295). Οι πιο συνηθισμένοι μετατροπείς είναι: d,i Μετατροπή σε προσημασμένο ακέραιο. u,o,x,x Μετατροπή σε μη-προσημασμένο ακέραιο ο οποίος θα τυπώθεί ως δεκαδικός (u), οκταδικός (o), δεκαεξαδικός με πεζά (x) ή δεκαεξαδικός με κεφαλαία (X). f,f,e,e,g,g,a,a Μετατροπή σε τύπο double ο οποίος θα τυπωθεί ως αριθμός με δεκαδικά ψηφία (f,f), σε επιστημονική αναπαράσταση με βάση και εκθέτη (e,e), σε κάποια από τις δύο προηγούμενες μορφές ανάλογα με κάποια κριτήρια (g,g) ή σε επιστημονική αναπαράσταση με βάση το 2 (a,a). Η διαφορά του πεζού μετατροπέα (f,e,g,a) από τον αντίστοιχο κεφαλαίο (F,E,G,A) είναι στην περίπτωση που πρέπει να εκτυπωθεί και κάποιος χαρακτήρας. Οι μετατροπείς της πρώτης ομάδας τον τυπώνουν πεζό ενώ της δεύτερης κεφαλαίο. c Μετατροπή σε χαρακτήρα. s Μετατροπή σε συμβολοσειρά. p Μετατροπή σε δείκτη. 9
n Δε θέλετε να μάθετε¹³. % Απλώς τυπώνει το %. Το παράδειγμα 7 ίσως είναι διαφωτιστικό: 1 # include <stdio.h> Listing 7: Διάφοροι μετατροπείς 2 3 int main() 4 { 5 int a = 74; 6 float c = 124.768; 7 8 printf(" Metatropeas d: %d\n", a); 9 printf(" Metatropeas i: %i\n", a); 10 printf(" Metatropeas u: %u\n", a); 11 printf(" Metatropeas o: %o\n", a); 12 printf(" Metatropeas x: %x\n", a); 13 printf(" Metatropeas X: %X\n", a); 14 printf(" Metatropeas c: %c\n", a); 15 printf(" Metatropeas p: %p\n", a); 16 17 printf(" Metatropeas f: %f\n", c); 18 printf(" Metatropeas g: %g\n", c); 19 printf(" Metatropeas e: %e\n", c); 20 printf(" Metatropeas a: %a\n", c); 21 22 23 return 0; 24 } Αν το τρέξετε θα δείτε στην οθόνη το παρακάτω: Metatropeas d: 74 Metatropeas i: 74 Metatropeas u: 74 Metatropeas o: 112 Metatropeas x: 4a Metatropeas X: 4A Metatropeas c: J Metatropeas p: 0x4a Metatropeas f: 124.767998 Metatropeas g: 124.768 ¹³Αφού επιμένετε: Η παράσταση θεωρείται δείκτης σε έναν μη-προσημασμένο ακέραιο. Σε αυτόν η printf γράφει πόσους χαρακτήρες έχει τυπώσει μέχρι εκείνη τη στιγμή. 10
Metatropeas e: 1.247680e+02 Metatropeas a: 0x1.f3126ep+6 Οι προσδιοριστές d και i είναι το ίδιο πράγμα. Επειδή ο αριθμός 74 είναι θετικός ακέραιος δεν αλλάζει κάτι αν τον θεωρήσει κανείς μη-προσημασμένο (τρίτη γραμμή, μετατροπέας u). Η τέταρτη γραμμή είναι ο αριθμός 74 σε οκταδικό σύστημα (1 64 + 1 8 + 2 = 74). Στην πέμπτη και έκτη γραμμή είναι η δεκαεξαδική αναπαράσταση του αριθμού (4 16 + 10 = 74) με πεζό και κεφαλαίο το a αντίστοιχα. Στην επόμενη βλέπουμε ότι ο χαρακτήρας με κωδικό ASCII 74 είναι ο J. Και στο τέλος βλέπουμε τη δεκαεξαδική αναπαράσταση του 74 με το πρόθεμα 0x. Προαιρετικά στους προσδιοριστές μπορούμε να χρησιμοποιήσουμε πριν από το μετατροπέα έναν τροποποιητή μεγέθους. Αυτοί είναι¹⁴: hh Σημαίνει ότι ο επόμενος μετατροπέας αναφέρεται σε έναν χαρακτήρα. h Ο επόμενος μετατροπέας αναφέρεται σε έναν short. l Ο επόμενος μετατροπέας αναφέρεται σε έναν long int. ll Ο επόμενος μετατροπέας αναφέρεται σε έναν long long int. L Ο επόμενος μετατροπέας αναφέρεται σε έναν long double. Συνοψίζοντας, για κάθε πιθανό τύπο και προσδιοριστή μεγέθους (long/short, signed/unsigned) θα πρέπει να χρησιμοποιούμε κάποιον από τους παρακάτω προσδιοριστές όταν τυπώνουμε στην οθόνη ή τους ζητάμε στο πληκτρολόγιο. Στην περίπτωση των ακεραίων, χρησιμοποιούμε %d για προσημασμένους αριθμούς και %u για μη-προσημασμένους. Επίσης χρησιμοποιούμε τον προσδιοριστή x αν θέλουμε ο αριθμός να τυπωθεί σε δεκαεξαδικό σύστημα ή o για οκταδικό. Και στις δύο περιπτώσεις ο αριθμός θεωρείται μη-προσημασμένος. Στην περίπτωση των δεκαξαδικών μπορεί να χρησιμοποιηθεί ο προσδιοριστής X για να τυπώνονται οι χαρακτήρες κεφαλαίοι. Για αριθμούς κινητής υποδιαστολής χρησιμοποιούμε τους προσδιοριστές f, g, e ανάλογα με τη μορφή που θέλουμε να τυπωθεί ή αντίστοιχα F, G, E αν θέλουμε οι χαρακτήρες σε αυτήν την εκτύπωση να τυπωθούν με κεφαλαία. 6 Τελεστές Η C παρέχει μία πληθώρα τελεστών. Σε γενικές γραμμές μπορούμε να τους διακρίνουμε σε αριθμητικούς, συσχετιστικούς, λογικούς, bit-τελεστές και κάποιους ειδικούς τελεστές που δεν μπορούν να ενταχτούν σε κάποια από τις παραπάνω καθώς εκτελούν ιδιαίτερες λειτουργίες. 6.1 Αριθμητικοί τελεστές Οι αριθμητικοί τελεστές πραγματοποιούν αριθμητικές πράξεις στα ορίσματά τους. Μπορούμε και αυτούς περαιτέρω να τους διακρίνουμε σε κατηγορίες. Υπάρχουν για παράδειγμα δυαδικοί τελεστές δηλαδή τελεστές που επιδρούν σε δύο ορίσματα και δίνουν ένα αποτέλεσμα σε αντιδιαστολή με ¹⁴Υπάρχουν και οι j, z, t στους οποίους δε θα αναφερθούμε. 11
Πίνακας 2: Προσδιοριστές εισόδου/εξόδου και αντίστοιχοι τύποι Τύπος Προσδιοριστής int %d long int %ld long long int %lld short int %hd float %f double %lf long double %Lf char %c τους μοναδικούς τελεστές οι οποίοι δέχονται ένα και μόνο όρισμα. Οι τελεστές αυτοαύξησης και αυτομείωσης (οι οποίοι είναι μοναδικοί) μπορούν να χρησιμοποιηθούν είτε ως προθεματικοί (πριν το όρισμά τους) είτε ως μεταθεματικοί (μετά το όρισμά τους). Πίνακας 3: Αριθμητικοί τελεστές Τελεστής Πράξη Τύπος + Πρόσθεση Δυαδικός¹⁵ - Αφαίρεση Δυαδικός * Πολλαπλασιασμός Δυαδικός / Διαίρεση Δυαδικός % Ακέραιο υπόλοιπο Δυαδικός ++ Μοναδιαία αύξηση Μοναδικός -- Μοναδιαία μείωση Μοναδικός Στον πίνακα 6.1 αναγράφονται οι δυαδικοί αριθμητικοί τελεστές της γλώσσας και η λειτουργία τους. Οι τελεστές πρόσθεσης, αφαίρεσης, διαίρεσης και πολλαπλασιασμού έχουν τις προφανείς σημασίες. Ένα σημείο που είναι καλό να το έχει κανείς υπόψη του είναι ότι όταν τα ορίσματα είναι διαφορετικών τύπων τότε σιωπηρά η γλώσσα πραγματοποιεί κάποιες μετατροπές ώστε να μην υπάρχει απώλεια στην ακρίβεια του αποτελέσματος. Δηλαδή η τιμή του μικρότερου τύπου μετατρέπεται στον μεγαλύτερο. Για παράδειγμα αν πρόκειται να πραγματοποιηθεί η πράξη a + b και ο a είναι float ενώ ο b είναι long double, τότε η τιμή του a μετατρέπεται σε κάποιο προσωρινό χώρο αποθήκευσης σε long double και μετά προστίθεται με την τιμή του b. Τονίζουμε ότι η μεταβλητή a δεν αλλάζει αλλά η τιμή της αποθήκευεται σε έναν άλλο χώρο με διαφορετική μορφή. Μία ακόμα παρατήρηση για τους παραπάνω προφανείς τελεστές είναι ότι ο τελεστής διαίρεσης αποκόπτει τα δεκαδικά ψηφία του αποτελέσματος όταν τα ορίσματά του είναι ακέραιοι, δηλαδή πραγματοποιεί ακέραια διαίρεση. Επίσης ο τελεστής ακέραιου υπολοίπου πραγματοποιείται μόνο σε ακεραίους. Σε περίπτωση που δοθούν ορίσματα κινητής υποδιαστολής τότε αποκόπτεται το 12
δεκαδικό τους μέρος. Τέλος, οι τελεστές μοναδιαίας αύξησης και μείωσης δέχονται ένα μόνο όρισμα το οποίο αυξάνουν ή μειώνουν κατά μία μονάδα αντίστοιχα. Μπορούν να χρησιμοποιηθούν ως προθεματικοί τελεστές, δηλαδή πριν το όρισμά τους έτσι: ++a ή --a αλλά και ως μεταθεματικοί τελεστές, δηλαδή μετά το όρισμά τους, έτσι: a++ ή a--. Και στις δύο περιπτώσεις το αποτέλεσμα θα είναι το όρισμα να αυξηθεί ή να μειωθεί κατά μία μονάδα. Η διαφορά είναι ότι στην περίπτωση του προθεματικού τελεστή η αύξηση θα γίνει πριν η γλώσσα υπολογίσει την τιμή της έκφρασης ++a ενώ στην περίπτωση του μεταθεματικού τελεστή θα υπολογιστεί μετά. Δείτε το πρόγραμμα 8. 1 # include <stdio.h> 2 3 int main() 4 { 5 int a; 6 7 a = 2; Listing 8: Μοναδιαία αύξηση προ- και μεταθεματικά 8 printf("h a me metathematiko telesti ayxisis: %d\n", a++); 9 printf(" Telika i a eivai: %d\n", a); 10 11 a = 2; 12 printf("h a me prothematiko telesti ayxisis: %d\n", ++a); 13 printf(" Telika i a eivai: %d\n", a); 14 15 return 0; 16 } Οι γραμμές 7 έως 9 και 11 έως 13 κάνουν περίπου το ίδιο πράγμα: Θέτουν την τιμή 2 στη μεταβλητή a, μετά τυπώνουν την τιμή της έκφρασης a++ ή της ++a στις γραμμές 8 και 12 αντίστοιχα και τέλος τυπώνουν την τιμή της μεταβλητής a στις γραμμές 9 και 13. Αν τρέξετε το πρόγραμμα θα δείτε ότι τόσο στη γραμμή 9 όσο και στη γραμμή 13 η τελική τιμή της μεταβλητής a είναι 3. Δηλαδή, τελικά η τιμή της a αυξάνεται κατά μία μονάδα είτε χρησιμοποιηθεί ο προθεματικός είτε ο μεταθεματικός τελεστής. Η διαφορά είναι ότι η γραμμή 8 τυπώνει στην οθόνη 2 ενώ η γραμμή 12 τυπώνει 3. Ο λόγος είναι ότι στη γραμμή 8 πρώτα υπολογίζεται η τιμή της μεταβλητής a και δίνεται για επεξεργασία στην printf και μετά (αφότου τυπωθεί) αυξάνεται η τιμή της κατά 1. Ενώ στη γραμμή 12, πρώτα αυξάνεται η τιμή της μεταβλητής a κατά 1 και μετά αυτό που προκύπτει δίνεται στην printf για επεξεργασία. 6.2 Bit τελεστές Μια σειρά τελεστών στη C εκτελεί πράξεις στα επιμέρους bit των ορισμάτων τους. Οι τελεστές αυτοί εφαρμόζονται μόνο σε ορίσματα ακέραιου τύπου. Στον πίνακα 6.2 φαίνονται οι τελεστές αυτοί και η πράξη που ο καθένας πραγματοποιεί. Όλοι είναι δυαδικοί τελεστές εκτός από τον ~ ο οποίος είναι προθεματικός μοναδικός τελεστής. Οι τελεστές &, και ^ εφαρμόζουν αντίστοιχα τις λογικές πράξεις ΚΑΙ, Η και ΑΠΟΚΛΕΙΣΤΙΚΟ Ή αντίστοιχα στα αντίστοιχα bit των ορισμάτων τους. Για πα- 13
Πίνακας 4: Bit τελεστές Τελεστής Πράξη & Λογικό ΚΑΙ Λογικό Ή ^ Λογικό ΑΠΟΚΛΕΙΣΤΙΚΟ Η ~ Συμπλήρωμα ως προς ένα << Αριστερή ολίσθηση >> Δεξιά ολίσθηση ράδειγμα ¹⁶ ισχύουν οι παρακάτω πράξεις μεταξύ των αριθμών 45 = (101101) 2 και 24 = (011000) 2 : 45 & 24 = 101101 & 011000 = 001000 = 8, 45 24 = 111101 = 61, 45 ^ 24 = 110101 = 53. Οι τελεστές << και >> ολισθαίνουν τα bits του πρώτου τους ορίσματος προς τα αριστερά ή τα δεξιά αντίστοιχα τόσες φορές όσες το δεύτερό τους όρισμα. Π.χ. 45 << 1 = 101101 << 1 = 011010 ή 45 << 2 = 101101 << 2 = 110100. Τα bits που χάνονται όταν βγαίνουν από τα αριστερά κατά την αριστερή ολίσθηση χάνονται στα αλήθεια. Τα bits που μένουν κενά από τα δεξιά κατά την αριστερή ολίσθηση γίνονται μηδέν. Για μη προσημασμένους ακέραιους η πράξη της ολίσθησης αριστερά κατά 1 είναι ισοδύναμη με τον πολλαπλασιασμό με το 2, η ολίσθηση αριστέρα κατά 2 είναι ισοδύναμη με τον πολλαπλασιασμό με το 4 κ.ο.κ. Αντίστοιχα, όταν πραγματοποιείται ολίσθηση προς τα δεξιά, τα bits που βγαίνουν από το δεξί άκρο χάνονται. Τα bits που μένουν κενά από τα αριστερά γεμίζουν με μηδενικά. Η ολίσθηση προς τα δεξιά κατά μία θέση σε μη προσημασμένους ακεραίους είναι ισοδύναμη με την ακέραια διαίρεση με το δύο, η ολίσθηση κατά δύο θέσεις είναι ισοδύναμη με τη διαίρεση με το τέσσερα κ.ο.κ. Όλα αυτά είναι σχεδόν προφανή για τους μη προσημασμένους ακέραιους τύπους και σχετίζονται διαισθητικά με την πράξη του πολλαπλασιασμού και της διαίρεσης με το δύο αλλά τι γίνεται με τους προσημασμένους; Θυμηθείτε ότι σε μορφή συμπληρώματος ως προς δύο το πιο αριστερό bit παριστάνει το πρόσημο οπότε ο αριθμός (1111) 2 είναι το 1 σε έναν φανταστικό 4-bit υπολογιστή. Αν αυτό ολισθήσει δεξιά κατά 1 και το κενό που μένει αριστερά γεμίσει με το μηδέν, τότε ο αριθμός αυτός γίνεται (0111) 2 = 7 οπότε χάνεται το θέμα του πολλαπλασιασμού. Προκειμένου να μην εμπλακεί σε τέτοιου είδους διλήμματα τα οποία ίσως δεν έχουν και πρακτικό νόημα, το πρότυπο της C δεν ορίζει τι γίνεται σε περιπτώσεις ολίσθησης αρνητικών προσημασμένων ακέραιων. Οπότε τελικά, οι τελεστές αυτοί είναι φρόνιμο να χρησιμοποιούνται σε μη-προσημασμένους ακέραιους τύπους. Τέλος, ο τελεστής συμπληρώματος ως προς ένα ~ αντιστρέφει τα bits του ορίσματός του, δηλαδή ~(1101) = 0010. Δείτε τους τελεστές σε δράση τρέχοντας το παρακάτω παράδειγμα: 1 # include <stdio.h> Listing 9: Bit τελεστές ¹⁶Στα παρακάτω παραδείγματα το ίσον χρησιμοποιείται με την έννοια των μαθηματικών και όχι ως ο τελεστής ανάθεσης τιμής της C. 14
2 3 int main() 4 { 5 unsigned int a = 45, b = 24; 6 7 printf("a: %u, b: %u\n", a, b); 8 printf("a & b: %u\n", a & b); 9 printf("a b: %u\n", a b); 10 printf("a ^ b: %u\n", a ^ b); 11 printf("~a: %u\n", ~a); 12 printf("a << 2: %u\n", a << 2); 13 printf("a >> 2: %u\n", a >> 2); 14 15 return 0; 16 } 6.3 Συσχετιστικοί τελεστές Είναι οι τελεστές που χρησιμοποιούνται οπουδήποτε απαιτείται μία λογική συνθήκη η οποία είτε ισχύει είτε όχι. Μπορεί κανείς να τους θεωρήσει ως δυαδικούς τελεστές οι οποίοι επιστρέφουν τη λογική τιμή αληθής ή τη λογική τιμή ψευδής όποτε ισχύει μία συγκεκριμένη σχέση ανάμεσα στα δύο τους ορίσματα. Οι τελεστές φαίνονται στον πίνακα 6.3 Ένα σημείο που πρέπει να τονιστεί Πίνακας 5: Συσχετιστικοί τελεστές Τελεστής Σχέση Τελεστής Σχέση < Μικρότερο <= Μικρότερο ή ίσο > Μεγαλύτερο >= Μεγαλύτερο ή ίσο == Ίσο!= Διάφορο είναι ότι στην πραγματικότητα δεν υπάρχουν ιδιαίτερες τιμές για τις παραστάσεις αληθής και ψευδής. Η ακέραια τιμή 0 θεωρείται ότι σημαίνει ψευδής ενώ οποιαδήποτε μη-μηδενική ακέραια τιμή θεωρείται αληθής. Οπότε τελικά οι συσχετιστικοί τελεστές μπορούν να θεωρηθούν ως ειδικού τύπου αριθμητικοί τελεστές. Δηλαδή ο τελεστής < μπορούμε να θεωρήσουμε ότι είναι ένας αριθμητικός τελεστής που δίνει αποτέλεσμα 1 όταν το πρώτο του όρισμα είναι μικρότερο από το δεύτερο και 0 όταν αυτό δεν ισχύει. Επίσης μεγάλη προσοχή χρειάζεται και στον τελεστή ισότητας == τον οποίο μπορεί κανείς να μπερδέψει εύκολα με τον τελεστή απόδοσης τιμής =. Ο πρώτος ελέγχει αν τα δύο του ορίσματα είναι ίσα και επιστρέφει 1 αν είναι ή 0 αν δεν είναι. Ο τελεστής απόδοσης τιμής από την άλλη μεριά υπολογίζει την τιμή του δεύτερου ορίσματός του και το αποθηκεύει στη θέση μνήμης που αντιστοιχεί στο πρώτο όρισμα. Η πράξη αυτή θεωρείται ως έκφραση η οποία έχει τιμή ίση με την τιμή που τελικά παίρνει το αριστερό όρισμα. Οπότε αν κανείς θέλει να ελέγξει ότι η μεταβλητή a 15
έχει την τιμή 5 και αντί να γράψει a == 5 γράψει a = 5 τότε η γλώσσα θα θεωρεί ότι αυτή η συνθήκη πάντα αληθεύει καθώς το 5 είναι μία μη-μηδενική τιμή οπότε αληθής. 6.4 Λογικοί τελεστές Λογικές εκφράσεις που προκύπτουν με τελεστές όπως αυτοί της προηγούμενης ενότητας μπορούν να συνδυαστούν με λογικούς τελεστές οι οποίοι είναι το λογικό ΚΑΙ &&, το λογικό Ή και το λογικό ΟΧΙ (λογική άρνηση)!. Για παράδειγμα η έκφραση a == 2 && b == 5 αληθεύει αν η a είναι ίση με 2 και η b είναι ίση με 5. Η έκφραση a == 2 b == 5 αληθεύει αν η a είναι ίση με 2 ή αν η b είναι ίση με 5. Τέλος ο λογικός τελεστής! αντιστρέφει τη λογική τιμή του ορίσματός του, δηλαδή αν μία έκφραση a είναι αληθής, τότε η!a είναι ψευδής και το αντίστροφο. Ο τελεστής! θέλει ιδιαίτερη προσοχή καθώς έχει υψηλότερη προτεραιότητα από τους συσχετιστικούς τελεστές οπότε η έκφραση!a > -1 δεν σημαίνει!(a > -1) αλλά (!a) > -1 η οποία θα είναι πάντα αληθής ανεξάρτητα από την τιμή του a. 6.5 Τελεστές ανάθεσης Εκτός από τον απλό τελεστή ανάθεσης = υπάρχουν και οι τελεστές +=, -=, /=, *=, %=, &=, =, ^=, <<= και >>=. Ο τελεστής += για παράδειγμα προσθέτει στο πρώτο του όρισμα το δεύτερο, δηλαδή η εντολή a += 5; προσθέτει στη μεταβλητή a την τιμή 5. Είναι κατά κάποιο τρόπο μία συντόμευση της εντολής a = a + 5; που θα έγραφε κανείς σε μία πιο φιλική γλώσσα. Κατά αντίστοιχο τρόπο λειτουργούν και οι υπόλοιποι τελεστές. Μία ιδιαιτερότητα του τελετή απόδοσης τιμής στη C είναι ότι μπορεί να χρησιμοποιηθεί σε μία αλυσίδα αναθέσεων π.χ. σαν αυτήν: a = b = c = 5; η οποία εκτελείται από τα δεξιά προς τα αριστερά και έχει σαν αποτέλεσμα η τιμή 5 να αποθηκευτεί στη μεταβλητή c, στη συνέχεια η τιμής αυτής (5) να αποθηκευτεί στη b και στη συνέχεια η τιμή αυτής να αποθηκευτεί στην a. 6.6 Άλλοι τελεστές Υπάρχουν διάφοροι άλλοι τελεστές στη C (όχι πολλοί) όπως ο τελεστής κόμμα,, ο τελεστής απόδοσης τιμής υπό συνθήκη?, ο τελεστής sizeof, ο τελεστής [] για δεικτοδότηση πίνακα, ο τελεστής διεύθυνσης &, ο τελεστής αποαναφοροποίησης *, ο τελεστής μέλους struct. και ο τελεστής -> για τους οποίους θα μιλήσουμε αργότερα. 7 Εύρος τιμών Αναφέρθηκε ότι το πρότυπο της C θέτει κάποιους περιορισμούς αναφορικά με το εύρος τιμών των τύπων της γλώσσας αλλά δεν ορίζει επακριβώς το εύρος αυτό. Τα ακριβή όρια ορίζονται από το μεταγλωττιστή στον οποίο είναι υποχρεωτικό να υπάρχουν οι σχετικοί ορισμοί και να είναι προσβάσιμοι από τον προγραμματιστή. Τα όρια αυτά για τους ακέραιους βρίσκονται στο αρχείο συμπερίληψης limits.h και τα αντίστοιχα για τους αριθμούς κινητής υποδιαστολής στο float.h. Δείτε το παρακάτω πρόγραμμα: 16
1 # include <stdio.h> 2 # include <limits.h> 3 # include <float.h> Listing 10: Όρια long long int 4 5 int main() 6 { 7 printf(" Maximum unsigned long long int: % llu\n", ULLONG_MAX); 8 printf(" Minimum long long int: % lld\n", LLONG_MIN); 9 printf(" Maximum long long int: % lld\n", LLONG_MAX); 10 11 printf("\ nmaximum unsigned long int: % lu\n", ULONG_MAX); 12 printf(" Minimum long int: % ld\n", LONG_MIN); 13 printf(" Maximum long int: % ld\n", LONG_MAX); 14 15 printf("\ nmaximum unsigned int: %u\n", UINT_MAX); 16 printf(" Minimum int: %d\n", INT_MIN); 17 printf(" Maximum int: %d\n", INT_MAX); 18 19 printf("\ nmaximum unsigned short int: % hu\n", USHRT_MAX); 20 printf(" Minimum short int: % hd\n", SHRT_MIN); 21 printf(" Maximum short int: % hd\n", SHRT_MAX); 22 23 printf("\ nmaximum unsigned char: %u\n", UCHAR_MAX); 24 printf(" Minimum signed char: %d\n", SCHAR_MIN); 25 printf(" Maximum signed char: %d\n", SCHAR_MAX); 26 27 printf(" Minimum char: %d\n", CHAR_MIN); 28 printf(" Maximum char: %d\n", CHAR_MAX); 29 30 printf(" Minimum long double: % Lf\n", LDBL_MIN); 31 printf(" Maximum long double: % Lf\n", LDBL_MAX); 32 33 printf(" Minimum double: % lf\n", DBL_MIN); 34 printf(" Maximum double: % lf\n", DBL_MAX); 35 36 printf(" Minimum float: %f\n", FLT_MIN); 37 printf(" Maximum float: %f\n", FLT_MAX); 38 39 return 0; 40 } Στην έξοδό του δίνει: Maximum unsigned long long int: 18446744073709551615 Minimum long long int: -9223372036854775808 17
Maximum long long int: 9223372036854775807 Maximum unsigned long int: 18446744073709551615 Minimum long int: -9223372036854775808 Maximum long int: 9223372036854775807 Maximum unsigned int: 4294967295 Minimum int: -2147483648 Maximum int: 2147483647 Maximum unsigned short int: 65535 Minimum short int: -32768 Maximum short int: 32767 Maximum unsigned char: 255 Minimum signed char: -128 Maximum signed char: 127 Minimum char: -128 Maximum char: 127 Minimum long double: 0.000000 Maximum long double: 1189731495357231765021263853030970205169063322294624200440323 7338917370055229707226164102903365288828535456978074955773144274431536702884341981 2557385374367867359320070697326320191591828296152436552951064679108661431179063216 9778838896134786560600399148753433211454911160088679845154866512852340149773037600 0091254793939662231513836224178385427439178381387178058894875405751682263476592355 7697480511372564902088485522249479139937758502601177354918009979622602685950855888 3608159846900235645132346594476384939859276456284579661772930407806609229102715046 0853880879593277816229868275478307680800401506949423034117289577771003357140105597 7524212405734700738625166011082837911962300846927720096515350020847447079244384854 5912886723000619085126472111951361467527633519562927597957250278002980795904193139 6030214709970352764674455309220226796562809914982320833296412410385092391847347861 2192169721054348428704835340811304257300221642134891734717423480071488075100206439 0517234247656004721768096486107994943415703476320643558624207443504424380566136017 6088374781653890278095769759772868600714870282879555671414046326158326236027628963 1617397848425448686060994827086796804807870251185893083854658422304090880599629459 4586201903766048446790926002225410530775901065760671347200125846406957030257138960 9837579989269545530523685607586831792231136395194688508807718721047052039575874800 1314313144425494391994017575316933939236688185618912993172910425292123683515992232 2050998001677102784035360140829296398115122877768135706045789343535451696539561254 0488464471697868932116710872290880827783505182288576460622187397028516550837209923 4948333443522898475123275372663606621390228126470623407535207172405866507951821730 3463782631353393706774901950197841690441824738063162828586857741432581165364040218 4027249133933209492194984224427304270198730445366203502623869578046820036014472919 18
9712309553005720614186697485284685618651483271597448120312194675168637934309618961 5107330065552421485195201762858595091051839472502863871632494167613804996319791441 8702543027067584951920088379151694015817400467114778772014596444611752040594535047 6472180797576111172084627363927960033967047003761337450955318415007379641260504792 3251661354841291884211340823015473304754067072818763503617332908005951896325207071 6739045477771296822652062256514399193768044002923809031124379126147762559646942219 8137514696707944687035800439250765945161837981185939204954403611491531078225107269 1486979809240946772142727012404377187409216756613634938900451232351668146089322400 6979931760178053381918499819330084109859939387602926013909114145260037202848721324 1195542428210183120421610446740462163533690058366460659115629876474552506814500393 2941404131495400677602951005962253022823003631473824681059648442441324864573137437 5950964161680480241293518762046681356368775328146755387988717718365128939471953350 6188500326760735438867336800207438784965701457609034985757124304510203873049485425 6702479339322809110526041538528994849203991091946129912491633289917998094380337879 5220931314669461497059396641523759492858909604899161219449899863848370224866722491 4892467841020618336462741696957630763248023558797524525373703543388296086275342774 0016333434055083537048507374544819754722228975281083020898682633020285259923084168 0545396879114182976299889645764827652875045628549242651652177507995162596692291149 7778896235667095662713848201819134832168799586365263762097828507009933729439678463 9879024914514222742527006363942327998483976739987154418554201562244154926653014515 5046854892586202760857618371297633587612153825651296335381416639495165560002641591 8655485005705261143195291991880795452239464962763563017858089669222640623538289853 5867595990647008385687123810329591926494846250768992258419305480763620215089022149 2205280698420183508405869384938154989094454619778930291135765167754062322782983140 3347327660395223160342282471752818181884430488092132193355086987339586127607367086 6652375555675803171490108477320096424318780070008797346032906278943553743564448851 9071916164551411557619393996907674151564028265436640267600950875239455073415561358 6793306603174472092444651353236664764973540085196704077110364053815007348689179836 4049570606189535005089840913826869535090066783324472578712196604415284924840041850 9328119089636341757398971665960007594878006191640948543387585206571165410722609962 8815012314437794400874930194474433078438899570184271000480830501217712356062289507 6269042856800047718893158089358515593863176652948089031267747029662545110861548958 3950877967554641379448959605279752098748138397625785921057562844017593493241621483 3956535018919681138909184379573470326940634289008780584694035245347939808067427323 6297887100867175802531561302356064878709259865288416350972529537091114317204887747 4055390540094253754241193179441751370646896438615177188498670103415325423859110896 2471088538580868883777725864856414593426212108664758848926003176234596076950884914 9662444156604419552086811989770240.000000 Minimum double: 0.000000 Maximum double: 179769313486231570814527423731704356798070567525844996598917476803 1572607800285387605895586327668781715404589535143824642343213268894641827684675467 0353751698604991057655128207624549009038932894407586850845513394230458323690322294 8165808559332123348274797826204144723168738177180919299881250404026184124858368.000000 19
Minimum float: 0.000000 Maximum float: 340282346638528859811704183484516925440.000000 20