Τμήμα Ηλεκτρονικών Μηχανικών Τ.Ε.Ι. Κρήτης Προγραμματισμός Η/Υ (ΤΛ2007 ) Δρ. Μηχ. Νικόλαος Πετράκης (npet@chania.teicrete.gr) Πέμπτη (5 η ) τρίωρη διάλεξη. Ιστοσελίδα Μαθήματος: https://eclass.chania.teicrete.gr/ Εξάμηνο: Εαρινό 2016-17
Μετατροπή τύπων Όταν οι μεταβλητές/σταθερές είναι του ιδίου τύπου το αποτέλεσμα είναι γενικά του αυτού τύπου. Όταν όμως οι μεταβλητές/σταθερές σε μία έκφραση δεν είναι του ιδίου τύπου τότε λέμε ότι έχουμε μία μικτή έκφραση. Στη C υπάρχουν μερικοί απλοί κανόνες αυτόματης μετατροπής τύπων στις μικτές εκφράσεις. Γενικά ο κανόνας είναι να μετατρέπεται ο τύπος με το μικρότερο μέγεθος στον τύπο με το μεγαλύτερο μέγεθος έτσι ώστε να μη χάνεται πληροφορία. Για παράδειγμα εάν f είναι float και i είναι integer τότε στην έκφραση f+i o i μετατρέπεται σε float. Η έκφραση υπολογίζεται στον τύπο με το μεγαλύτερο μέγεθος Το αποτέλεσμα είναι τύπου όμοιου με τον «μεγαλύτερο» τύπο στην έκφραση. Εκφράσεις που αναθέτουν ένα τύπο μεγαλύτερου μεγέθους σε ένα μικρότερο (π.χ. i = f) συνήθως προκαλούν την δημιουργία μιας προειδοποίησης (warning) από τον μεταφραστή (compiler) και γενικά πρέπει να αποφεύγονται. 2
Μέγεθος Πίνακας μετατροπής τύπων Ο παρακάτω πίνακας δείχνει τον τύπο του αποτελέσματος σαν συνάρτηση των τύπων στην έκφραση. Μέγεθος Operand types short int ή short long int ή int float double long double short int ή short short int ή short long int ή int float double long double long int ή int long int ή int long int ή int float double long double float float float float double long double double double double double double long double long double long double long double long double long double long double 3
Μετατροπή τύπων κατά την ανάθεση Οι μετατροπές τύπων συμβαίνουν και με τον τελεστή ανάθεσης π.χ. Α = Β Η έκφραση στα δεξιά του τελεστή (=) προβιβάζεται στον τύπο της μεταβλητής στα αριστερά του τελεστή ανάθεσης Αυτό όμως μπορεί να προκαλέσει προβλήματα όπως αναφέρθηκε προηγουμένως αν η μεταβλητή είναι «χαμηλότερου» τύπου. Παράδειγμα. Εάν d είναι double, και i είναι int τότε η ανάθεση d = i; Θα προκαλέσει την μετατροπή της ακέραιας μεταβλητής i σε τύπο double ώστε η ανάθεση να επιτευχθεί. Όμως η ανάθεση i = d θα προκαλέσει τον υποβιβασμό της double μεταβλητής d σε τύπο int οπότε το δεκαδικό μέρος της d θα χαθεί. 4
Σφάλματα υπολογισμών σε εκφράσεις 563.8 + 631.9 1195.7 1196 + 321.6 1517.6 1518 Σφάλματα στον υπολογισμό αριθμητικών εκφράσεων μπορούν να παρουσιασθούν λόγω Χρήσης περιορισμένου αριθμού ψηφίων Χρήσης τεχνικών προσέγγισης της τιμής μιας συνάρτησης ή έκφρασης αντί του άμεσου υπολογισμού της τιμής της321.6 Παράδειγμα + 631.9 Η πρόσθεση των αριθμών 563.8, 631.9, και 321.6 953.5 όταν είμαστε 953.5 περιορισμένοι σε τέσσερα ψηφία έχει σαν αποτέλεσμα + 1518 563.8 όταν προσθέσουμε 563.8, 631.9, και 321.6, και αποτέλεσμα 1517 όταν προσθέσουμε 321.6, 631.9, και 563.8. 1517.3 1517 Από το παράδειγμα βλέπουμε ότι η διάταξη των όρων του αθροίσματος επηρεάζει το τελικό αποτέλεσμα. Η σωστή απάντηση είναι 1517.3. 5
Είσοδος και έξοδος χαρακτήρων text stream ή file (ρεύμα κειμένου) stdin - πρότυπη είσοδος (standard input) stdout - πρότυπη έξοδος (standard output) char c; c = getchar ( ); putchar (c); // function prototypes // int getc (FILE *stream); // int getchar (void); // int putc (int c, FILE *stream); // int putchar (int c); 6
Πληθώρα ασκήσεων χωρίς άλλη γνώση για είσοδο ή έξοδο Αντιγραφή αρχείων κειμένου Μέτρηση χαρακτήρων αρχείου κειμένου Μέτρηση γραμμών αρχείου κειμένου Μέτρηση λέξεων αρχείου κειμένου 7
Αντιγραφή αρχείων διάβασμα ενός χαρακτήρα ενόσω (ο χαρακτήρας δεν είναι ένδειξη τέλους αρχείου) έξοδος του χαρακτήρα που μόλις διαβάστηκε διάβασμα ενός χαρακτήρα #include <stdio.h> /* αντιγραφή της εισόδου στην έξοδο */ main() { char c; } c = getchar(); while ( c!= EOF) { putchar(c) ; c = getchar(); } printf ("\nπληκτρολόγησε ό,τι θέλεις και \ ENTER (ή CTRL+Z για τέλος):\n"); while ( ( c =getchar () )!= EOF) putchar (c) ; 8
Μέτρηση γραμμών αρχείου κειμένου /* Μέτρηση γραμμών της πρότυπης εισόδου */ #include <stdio.h> main() { char ch; int nl=0; printf ("\nπληκτρολόγησε ό,τι θέλεις \ και ENTER (ή CTRL+Z για τέλος):\n"); while ((ch = getchar())!= EOF) if (ch == \n ) nl++; printf ("Υπήρχαν %d γραμμές.\n",nl); } 9
Άσκηση Να γίνει ένα πρόγραμμα το οποίο να αντιγράφει την πρότυπη είσοδο (stdin) στην πρότυπη έξοδο (stdout) χαρακτήρα χαρακτήρα μέχρι να συναντήσει το «σύμβολο τέλους αρχείου», μετατρέποντας όμως όλους τους πεζούς χαρακτήρες σε κεφαλαίους. 11
Μέτρηση λέξεων αρχείου κειμένου #include <stdio.h> // Μέτρηση λέξεων, γραμμών και #define OUT 0 // χαρακτήρων της πρότυπης εισόδου #define IN 1 // White spaces: \t \n main() { char ch; int nc=0,nw=0,nl=0,status=out; printf ("Πληκτρολόγησε ό,τι θέλεις... :\n"); while ((ch = getchar())!= EOF) { nc++; if (ch == \n ) nl++; if (ch == ch == \t ch == \n ) status=out; if (status==out) { nw++; status = IN; } } printf("υπήρχαν %d λέξεις, %d γραμμές και %d χαρακτήρες.\n",nw, nl, nc); } 12
Φωλιασμένα if (nested if) προηγούμενη_εντολή; if (λογική_έκφραση 1 ) εντολή 1 ; if (λογική_έκφραση 2 ) εντολή 2 ; if (λογική_έκφραση 3 ) εντολή 3 ;... if (λογική_έκφραση N ) εντολή N ; εναλλακτική_εντολή; προαιρετικό επόμενη_εντολή; 13
Η «μετέωρη» εντολή συνθήκης if Θα δούμε παρακάτω ένα κοινό λάθος στον προγραμματισμό της εντολής συνθήκης if-then-. Αρχικά, ας θεωρήσουμε μια σωστή δομή εντολής if, η οποία είναι σύνθετη (καθένα από τα if/ blocks της εντολής περιέχει με τη σειρά του μια άλλη εντολή if): if (λογική_έκφραση A ) if (λογική_έκφραση B ) εντολή 1 ; εντολή 2 ; if (λογική_έκφραση C ) εντολή 3 ; εντολή 4 ; 14
Ερμηνεία με διάγραμμα ροής if (λογική_έκφραση A ) if (λογική_έκφραση B ) εντολή 1 ; εντολή2 ; True Α False if (λογική_έκφρασηc ) εντολή 3 ; εντολή4 ; Συνθήκη True Β False True C False E1 E2 E3 E4 15
Εσφαλμένη παραλλαγή του προγράμματος Ας υποθέσουμε τώρα ότι δεν χρειαζόμαστε την εντολή 2. Τότε το διάγραμμα ροής θα γίνει: True Α False Δεν ξέρουμε αν αντιστοιχεί στο πρώτο ή στο δεύτερο if if (λογική_έκφραση A ) if (λογική_έκφραση B ) εντολή 1 ; if (λογική_έκφρασηc ) εντολή 3 ; εντολή4 ; Συνθήκη True Β False True C False E1 E3 E4 16
Σωστή παραλλαγή του προγράμματος Αν η Α αληθής και Β ψευδής τότε τερματίζει το πρόγραμμα True Α False Άγκιστρα if (λογική_έκφραση A ) { if (λογική_έκφρασηb ) εντολή 1 ; } if (λογική_έκφρασηc ) εντολή 3 ; εντολή4 ; Συνθήκη E1 True Β False E3 True C False E4 Καλή πρακτική: Χρήση άγκιστρων {} στις εντολές if ακόμη κι αν δεν απαιτούνται (δηλ. όταν ένα block if ή περιέχει μόνο μία εντολή) 17
Παρατηρήσεις Πρέπει να προσέχουμε διότι ο μεταφραστής δεν καταλαβαίνει τη στοίχιση του προγράμματος. Κατά τη μεταγλώττιση ο μεταφραστής αντιστοιχεί το με το τελευταίο «ανοικτό» if. Οπότε εάν γράψουμε: if (λογική_έκφραση A) if (λογική_έκφραση B) εντολή1; if (λογική_έκφραση C) εντολή3; εντολή4; True E1 Β True False E3 True C E4 Α Συνθήκη False False 18
Πολλαπλές εντολές συνθήκης Μία συχνή παραλλαγή είναι η πολλαπλή εντολή συνθήκης που σχηματικά παρουσιάζεται ως εξής:? Συνθήκη-1 Συνθήκη-2 Συνθήκη-3 Συνθήκη-4 Υπόλοιπες περιπτώσεις E1 E2 E3 E4 E5 19
Στη C οι πολλαπλές εντολές συνθήκης προγραμματίζονται με το παρακάτω ιδίωμα: if (λογική_έκφρασηα) εντολή1; if (λογική_έκφρασηβ) /* Είναι η συνθήκη-2 */ εντολή2; if (λογική_έκφρασηc) /* Είναι η συνθήκη-3 */ εντολή3; if (λογική_έκφρασηd) /* Είναι η συνθήκη-4 */ εντολή4; εντολή5; /* Είναι η συνθήκη-1 της προηγ. παραγράφου*/ 20
To παραπάνω ιδίωμα συνήθως γράφεται σαν: if (λογική_έκφρασηα)/*είναι η συνθήκη-1 της προηγ. παραγράφου*/ εντολή1; if (λογική_έκφρασηβ) /* Είναι η συνθήκη-2 */ εντολή2; if (λογική_έκφρασηc) /* Είναι η συνθήκη-3 */ εντολή3; if (λογική_έκφρασηd) /* Είναι η συνθήκη-4 */ εντολή4; εντολή5; 21
Εντολή πολλαπλής διακλάδωσης Εάν οι λογικές εκφράσεις στις συνθήκες είναι της μορφής a == 2, a==5, a==17 (δηλαδή η μεταβλητή που εξετάζεται στη συνθήκη είναι διακριτού τύπου (integer ή character), τότε υπάρχει και μία άλλη εντολή πολλαπλής διακλάδωσης. Ας θεωρήσουμε το παρακάτω πρόγραμμα: if (a == 2) statement1; if (a == 0 a == 3) statement2; if (a == 6) statement3; if (a == 8 a == 9) statement4; stattement5; switch (a) { case 2: statement1; break; case 0: case 3: statement2; break; case 6: statement3; break; case 8: case 9: statement4; break; default: statement5; break; ΠΡΟΣΟΧΗ: να μην παραλείπονται οι εντολές break! 22 }
Παράδειγμα # include <stdio.h> main() { int month, day, year; printf( Enter date (mm/dd/yyyy): ); scanf( %d/%d/%d, &month, &day, &year); printf ( %d, day); switch (day) { case 1: case 21: case 31: printf( st ); break; case 2: case 22: printf( nd ); break; case 3: case 23: printf( rd ); break; default: printf( th ); break; } } printf ( day of ); switch (month) { case 1: printf( January ); break; case 2: printf( February ); break; case 3: printf( March ); break; case 4: printf( April ); break; case 5: printf( May ); break; case 6: printf( June ); break; case 7: printf( July ); break; case 8: printf( August ); break; case 9: printf( September ); break; case 10: printf( October ); break; case 11: printf( November ); break; case 12: printf( December ); break; } printf (, %d..\n, year); return (0); 23
π.χ. αν δώσουμε την σημερινή ημερομηνία Enter date (mm/dd/yyyy): 3/30/2017 30th day of March, 2017.. Press any key to continue... ενώ για την πρωταπριλιά Enter date (mm/dd/yyyy): 4/1/2017 1st day of April, 2017.. Press any key to continue... 24
Εντολή επανάληψης do-while do { αρχικοποίηση μετρητή μεταβολή μετρητή επαναλαμβανόμενες_εντολές; } while (παράσταση ή συνθήκη); έλεγχος συνθήκης Σημείωση: Πρώτα εκτελώ μετά ελέγχω, άρα οι επαναλαμβανόμενες_εντολές θα εκτελεστούν τουλάχιστον μία φορά, ακόμα κι αν η συνθήκη είναι εξ αρχής ψευδής. 25
Παράδειγμα με do-while /* Υπολογισμός του αθροίσματος των τετραγώνων πραγματικών αριθμών μέχρι να δοθεί η τιμή -99.0 */ main() { double x=0.0, sum=0.0; int i=0; do { sum += x*x; ++i; printf( Δώστε τον %dο πραγματικό αριθμό :, i); scanf( %f,&x); }while (x!= -99.0); } printf( Το άθροισμα των τετραγώνων των αριθμών \ που δώσατε ισούται με %.3f \n\n, sum); 26