ΦΡΟΝΤΙΣΤΗΡΙΟ 1-β
ΕΝΤΟΛΗ ΑΝΑΘΕΣΗΣ Οι μεταβλητές λαμβάνουν τιμές με συκγεκριμένη εντολή ανάθεσης Η εντολή είναι της μορφής: Μεταβλητή = έκφραση όπου η έκφραση μπορεί να είναι μια σταθερή τιμή, ένας μαθηματικός τύπος, το αποτέλεσμα κλήσης μιάς συνάρτησης κλπ. Η έκφραση θα πρέπει να παράγει ένα αποτέλεσμα που να είναι συμβατό με τον τύπο της μεταβλητής
ΠΑΡΑΔΕΙΓΜΑ (1) #include <stdio.h> float main() { float height, base, area; height = 10.2; base = 3.6; printf(“The height of the triangle is: %f\n”, height); printf(“The base of the triangle is: %f\n”, base); area = (height * base)/2; printf(“The area of the triangle is %f\n”, area); return (area); }
ΠΑΡΑΔΕΙΓΜΑ (2) #include <stdio.h> float main() { float height, base, area; printf(“Enter the height of the triangle:”); scanf(“%f”, &height); printf(“\n”); printf(“Enter the base of the triangle”); scanf(%f”, &base); printf(“The height of the triangle is: %f\n”, height); printf(“The base of the triangle is: %f\n”, base); area = (height * base)/2; printf(“The area of the triangle is %f\n”, area); return (area); }
ΠΑΡΑΔΕΙΓΜΑ (3) #include <stdio.h> #define FREEZING_POINT_IN_FARHENHEIT 32 #define SCALE_FACTOR (5.0 / 9.0) float main() { float farhenheit, celcius; printf(“Enter the temperature in Farhenheit:”); scanf(“%f”, &farhenheit); celcius = (farhenheit – FREEZING_POINT_IN_FARHENHEIT) * SCALE_FACTOR; printf(“The celcius equivalent temperature of %f is %.1f \n”, farhenheit, celcius); return (area); }
ΟΝΟΜΑΤΑ ΜΕΤΑΒΛΗΤΩΝ Τα ονόματα των μεταβλητών σ΄ένα πρόγραμμα C πρέπει να αρχίζουν με γράμμα καί μπορουν να περιέχουν αλφαριθμητικούς χαρακτήρες. Επίσης δεν μπορουν να περιέχουν ειδικούς χαρακτήρες όπως #, &, * κλπ. Υπάρχουν συγκεκριμένα ονόματα τα οποία αναφέρονται σε λέξεις κλειδιά της C Τα ονόματα των μεταβλητών είναι ευαισθητα στην δομή τους (Κεφαλαίοι, μικροί χαρακτήρες) π.χ. AVariable ≠ Avariable
ΛΕΞΕΙΣ ΚΛΕΙΔΙΑ ΠΟΥ ΔΕΝ ΜΠΟΡΟΥΝ ΝΑ ΧΡΗΣΙΜΟΠΟΙΗΘΟΥΝ ΓΙΑ ΜΕΤΑΒΛΗΤΕΣ auto double int struct break else long switch case enum register typedef char extern return union const float short unsigned continue for Signed void default goto sizeof volatile do if static while
ΚΑΛΕΣ ΠΡΑΚΤΙΚΕΣ ΟΝΟΜΑΣΙΑΣ ΜΕΤΑΒΛΗΤΩΝ Χρήση μικρών χαρακτήρων (όχι κεφαλαίων) Μη χρήση μικρών και κεφαλαίων για δύο διαφορετικές μεταβλητές π.χ one καί ONE Χρήση μνημονικών ονομάτων π.χ. interestRate αντί ir Κατανοητή σύνταξη γιά πολύπλοκα ονόματα π.χ. interestRate, ή interest_Rate αντί interestrate
ΒΑΣΙΚΟΙ ΤΥΠΟΙ ΔΕΔΟΜΕΝΩΝ (1) Όπως είπαμε, η C είναι μία typed language όπου οι μεταβλητές ορίζονται να είναι συμβατές με κάποιο συγκεκριμένο τύπο. Υπάρχουν δύο μεγάλες κατηγορίες τύπων δεδομένων Βασικοί τύποι που προσφέρει η γλώσσα Τύποι που ορίζονται από τον χρήστη Στή C έχουμε τους παρακάτω βασικούς τύπους δεδομένων Integer types (ακέραιοι αριθμοί) Floating point types (πραγματικοί αριθμοί) Character types (σύμβολα π.χ. Γράμματα και στοιχεία) Οι σύνθετοι (aggregate) τύποι που ορίζονται από τον χρήστη μπορούν να παραχθούν σαν διανύσματα (arrays) ή/και κατασκευές (structures) άλλων βασικών ή συνθέτων τύπων ανάλογα με το πρόβλημα και τη σχεδίαση του αλγόριθμου που χρησιμοποιείται για την επίλυση του προβλήματος.
ΒΑΣΙΚΟΙ ΤΥΠΟΙ ΔΕΔΟΜΕΝΩΝ (2) Οι παρακάτω λέξεις κλειδιά χρησιμοποιούνται για τον ορισμό ακεραίων μεταβλητών. Οι όροι μέσα σε στις παρενθέσεις είναι προαιρετικοί. Οι διαφορετικοί συνδυασμοί αφορούν στό «εύρος» των διαφορετικών τιμών που μιά μεταβλητή ενός συγκεκριμένου τύπου μπορεί να αποθηκεύσει: char signed char unsigned char [signed] short [int] unsigned short [int] [signed] [int] (ένα από τα δύο πρέπει να ορισθεί) unsigned [int] [signed] long [int] unsigned long [int] Οι τύποι char, signed char, και unsigned char είναι μη αριθμητικοί τύποι και δεν μπορούν άμεσα να παρουσιασθούν σαν αριθμοί κατά την είσοδο/έξοδο δεδομένων του προγράμματος. Κωδικοποιούνται όμως εσωτερικά στον Η/Υ σαν ακέραιοι χρησιμοποιώντας το κώδικα ASCII. Οι τύποι short, int, και long θεωρούνται αυτόματα signed
ΒΑΣΙΚΟΙ ΤΥΠΟΙ ΔΕΔΟΜΕΝΩΝ (3) Το εύρος των τιμών που μπορεί να αποθηκεύσει μία μεταβλητή απο κάθε ένα από τους παραπάνω τύπους εξαρτάται απο τον συγκεκριμένο μεταφραστή και το συγκεκριμένο επεξεργαστή. Όμως σε κάθε περίπτωση θα πρέπει το μέγεθος που καταλαμβάνει η μεταβλητή στη μνήμη να είναι: Μέγεθος του char ≤ Μέγεθος του short int ≤ Μέγεθος int ≤ Μέγεθος του long int
ΒΑΣΙΚΟΙ ΤΥΠΟΙ ΔΕΔΟΜΕΝΩΝ (4) Οι λέξεις κλειδιά που χρησιμοποιούνται για τον ορισμό μεταβλητών πραγματικων αριθμών είναι float (πραγματικός αριθμός) double (διπλής ακριβείας πραγματικός) long double (διπλής ακρίβειας πραγματικός με ακρίβεια 15 δεκαδικών ψηφίων) Οι πραγματικοί αριθμοί στη C είναι απλά ένα υποσύνολο των πραγματικών αριθμών στα μαθηματικά μιάς και λόγω περιορισμών στη μνήμη του Η/Υ δεν μπορούμε να παρουσιάσουμε αριυμους πέρα από ένα όριο.
ΒΑΣΙΚΟΙ ΤΥΠΟΙ ΔΕΔΟΜΕΝΩΝ (5) (Borland 5.02 C) Τύπος Signed (με πρόσημο) Unsigned (χωρίς πρόσημο) char -128 έως 127 0 έως 255 short int -32768 έως 32767 0 έως 65535 int -231 έως 231 - 1 0 έως 232 - 1 long int float 10-38 έως 10+38 Ακρίβεια 7 δεκαδικών double 10-308 έως 10+308 Ακρίβεια 15 δεκαδικών long double 10-4932 έως 10+4932 Ακρίβεια 18 δεκαδικών
Δυαδικοί Αριθμοί Ένας δυαδικός αριθμός είναι μια ακολουθία από 0 και 1. Η μετατροπή ενός δυαδικού αριθμού b3b2b1b0b-1b-2b-3 είναι (b3 X 23 ) + (b2 X 22) + (b1 X 21) + (b0 X 20) + (b-1 X 2-1) + (b-2 X 2-2) + (b-3 X 2 -3) Οπότε (1011.101)2 = (11.625)10
Signed και Unsigned Στούς αριθμούς με πρόσημο (signed) όταν το πρώτο bit είναι 0 δηλώνει θετικό πρόσημο και όταν είναι 1 δηλώνει αρνητικό. Οι αρνητικές τιμές είναι γνωστές σαν συμπλήρωμα του 2 ή δυαδικό συμπλήρωμα. Για να παράγουμε το δυαδικό συμπλήρωμα ενός αριθμού λαμβάνουμε το 2n – x όπου n είναι το πλήθος των bits και χ είναι ο αριθμός του οποίου προσπαθούμε να υπολογίσουμε το δυαδικό συμπλήρωμα. Το πλεονέκτημα του δυαδικού συμπληρώματος είναι ότι δυαδικοί αριθμοί μπορούν να προστεθούν ή να αφαιρεθούν σαν να ήταν χωρίς πρόσημο. Π.χ. Δυαδική Παρουσίαση Δεκαδική Παρουσίαση 110 -2 + 011 +3 ______ _____ 001 +1
Signed και Unsigned (Παράδειγμα σε Ακεραίους) Δυαδικό Δεκαδικό χωρίς πρόσημο (unsigned) Δεκαδικό με πρόσημο (signed) 000 001 1 010 2 011 3 100 4 -4 101 5 -3 110 6 -2 111 7 -1
ΑΡΙΘΜΗΤΙΚΗ ΜΕ signed/unsigned Στή C δέν γίνεται αυτόματα ο έλεγχος για να διαπιστωθεί ότι το αποτέλεσμα μιάς πράξης είναι στά σωστά όρια του τύπου της μεταβλητής. Ένα παράδειγμα έιναι ότι 2147483647 + 1 = - 2147483648 δηλαδή (231 -1 ) + 1= -231 !!!
ΣΤΑΘΕΡΕΣ Οι μεταβλητές πού ορίζονται να έχουν σταθερές τιμές σ’ όλη τη διάρκεια του προγράμματος μπορεί να είναι ακέραιοι (int) ή πραγματικοί (float, double), ή χαρακτήρες (char). Μιά ακέραια σταθερή π.χ. 1234 είναι int. Μία long παρίσταται με την καταλληξη l ή L π.χ. 123456789L. Unsigned σταθερές δηλώνονται με την κατάλληξη U π.χ. 1234567UL. Ακέραιες σταθερές μπορούν να παρουσιαστούν και στο οκταδικό σύστημα με το πρόθεμα 0 (μηδέν) π.χ. ο δεκαδικός 31 είναι 037 εάν ορισθεί σαν οκταδικός, και 0x1f εάν ορισθεί στο δεκαεξαδικό σύστημα. Π.χ. 011 είναι οκταδικός και είναι ο αριθμός 9 στό δεκαδικό. Πραγματικές σταθερές ορίζονται με την κατάλληξη f. Οι χαρακτήρες σταθερές ορίζονταο μέ quotes (‘ ‘) π.χ. ‘a’. Η τιμή της σταθερής είναι η τιμή του χαρακτήρα στο σύστημα μετατροπής που χρησιμοποιεί ο συγκεκριμένος Η/Υ. Π.χ. Ο χαρακτήρας ‘0’ έχει τιμη στον κώδικα ASCII 48. Περισσότερα για σταθερές αργότερα στο μάθημα.
ΠΑΡΑΔΕΙΓΜΑΤΑ int anIntegerVariable; float aFloatVariable; char aCharVariable; double aDoubleVariable; #define MAXLINE 100 #define ASTRING “I am a string” unsigned short aVar; signed int aSignedInt; Για αρχή ας εστιάσουμε στους ακεραίους καί στούς πραγματικούς (με ή χωρίς πρόσημο)
ΠΑΡΑΔΕΙΓΜΑ #include <stdio.h> #define fahr1 0 #define fahr2 20 /* Τύπωσε τους βαθμους σε κλίμακα Κελσίου καί Φαρεναΐτ. /* void main() { int celcius1, celcius2, celcius3, celcius4; celcius1 = 5 * (fahr1 – 32) / 9; celcius2 = 5 * (fahr2 – 32) / 9; celcius3 = 5 * (fahr3 – 32) / 9; celcius4 = 5 * (fahr4 – 32) / 9; printf(“%d\t%d\n”, fahr, celcius) /* Τύπωσε ακέραιο, tab, ακέραιο */ } Το αποτέλεσμα θα είναι 0 -17 20 -6 40 4 60 15
ΕΝΤΟΛΕΣ ΕΙΣΟΔΟΥ/ΕΞΟΔΟΥ Ας δούμε δύο βασικές εντολές εισόδου / εξόδου καί συγκεκριμένα τις: printf, και την scanf
ΕΝΤΟΛΗ ΕΚΤΥΠΩΣΗΣ ΣΤΗΝ ΟΘΟΝΗ (standard output) Η σύνταξη της εντολής printf έιναι printf(“δήλωση φόρμας”, έκφραση1, έκφραση2 ...) όπου: Η δήλωση φόρμας ορίζει το πως θα φορμάρεται ή εκτύπωση, και το τι θα τυπώνεται στο standard output. H δήλωση φόρμας ορίζει επίσης το πώς οι τιμές των μεταβλητών που είναι να τυπωθούν, μετατρέπονται από τη εσωτερική δυαδική μορφή του Η/Υ στην επιθυμητή μορφή της εκτύπωσης.
ΠΑΡΑΔΕΙΓΜΑ void main() { int i, j; float x, y; i = 10; j = 20; printf( “ i = %d, j = %d, x = %f, y = %f\n” , i, j, x, y); } Δήλωση φόρμας Εκτύπωση σε ακεραίου Εκτύπωση σε πραγματικό Οι μεταβλητές για εκτύπωση Αποτέλεσμα εκτύπωσης : i = 10, j = 20, x = 43.289200, y = 5527.0000
ΤΥΠΟΙ ΜΕΤΑΤΡΟΠΗΣ Στη δήλωση φόρμας οι πιο κοινές παρακάτω μετατροπές ορίζονται: %d : Εκτύπωση ακεραίου %e : Εκτύπωση πραγματικού σε εκθετική μορφή %f : Εκτύπωση πραγματικού σε δεκαδική μορφή με δεκαδικά ψηφία %g : Εκτύπωση πραγματικού σε εκθετική ή σε δεκαδική μορφή ανάλογα με το μεγεθός του Συμπληρωματικά με τα παραπάνω, η γενική μορφή είναι : %m.pX όπου m δηλώνει τον ελάχιστο αριθμό «χαρακτήρων» πρός εκτύπωση, p δηλώνει τον αριθμό δεκαδικών ψηφίων για εκτύπωση, και Χ είναι ένα από %d, %f, %g. a alert bell – ηχητικό σήμα \n new line – νέα γραμμή \b backspace – κενό πρίν \t horizontal tab – οριζόντιο κενό συγκεκριμένου μήκους Δοκιμάστε το παρακάτω πρόγραμμα
ΠΑΡΑΔΕΙΓΜΑ int main() { int i = 40; float x = 839.21; printf(“|%d|%5d|%-5d|%5.3d|\n”, i, i, i, i); printf(“|%10.3f|%10.3e|%10g|\n”, x, x, x, x); return (1); }
ΕΝΤΟΛΗ ΕΙΣΟΔΟΥ ΑΠΟ ΤΟ ΠΛΗΚΤΡΟΛΟΓΙΟ (standard input) Η σύνταξη της εντολής scanf έιναι scanf(“δήλωση φόρμας”, έκφραση1, έκφραση2 ...) όπου: Η δήλωση φόρμας ορίζει το πως θα φορμάρεται ή εκτύπωση, και το τι θα διαβάζεται απο το standard input. H δήλωση φόρμας ορίζει επίσης το πώς οι τιμές των μεταβλητών θα διαβαστούν.
ΠΑΡΑΔΕΙΓΜΑ void main() { int i, j; float x, y; Διαφορά από το printf void main() { int i, j; float x, y; scanf(%d%d%f%f”, &i, &j, &x, &y); } Εάν ο χρήστης εισάγει 1, -20, .3, -4.0ε3 τότε η παραπάνω εντολή εισόδου θα διαβάσει και θα αναθέσει στη μεταβλητή i τον αριθμό 1, στη j το -20, στη x το 0.3, και στη y το -4000.0
ΕΚΦΡΑΣΕΙΣ (expressions) Οι εκφράσεις στη C είναι συντακτικές μορφές που παράγονται από το τον συνδυασμό μεταβλητών, σταθερών, και τελεστών (operators). Προς το παρόν θα ασχοληθούμε με ακέραιες και πραγματικές μεταβλητές. Μεταβλητές ακέραιου, και πραγματικού τύπου μπορούν να συνδυαστούν με αριθμητικούς τελεστές. Το σημαντικό στοιχείο είναι ο τύπος του αποτελέσματος που προκύπτει.
ΑΡΙΘΜΗΤΙΚΟΙ ΤΕΛΕΣΤΕΣ (+) Πρόσθεση (δυαδικός τελεστής – binary operator), και έκφραση θετικού προσήμου (μοναδιαίος τελεστής – unary operator) (-) Αφαίρεση (δυαδικός τελεστής – binary operator), και έκφραση αρνητικού προσήμου (μοναδιαίος τελεστής – unary operator) (*) Πολλαπλασιασμός (δυαδικός τελεστής – binary operator) (/) Διαίρεση (δυαδικός τελεστής – binary operator) (%) Υπόλοιπο διαίρεσης (γιά ακέραιους τύπους μόνο - (δυαδικός τελεστής – binary operator) Άλλοι τελεστές που θα δούμε αργότερα είναι οι λογικοί τελεστές, σχεσιακοί τελεστές, καί τελεστές δυαδικών ψηφίων (bitwise operators)
ΠΑΡΑΔΕΙΓΜΑTA 25 aVariable +x -anotherVariable a*b a/b (Παράγει ακέραιο αποτέλεσμα όταν οι τυποι των a, b, είναι ακέραιοι. count/n a+ (b+c)/32) + 12 (b-4ac)/2d a%b (η γνωστή μας σχέση a modulo b)
ΥΠΟΛΟΓΙΣΜΟΣ ΤΩΝ ΕΚΦΡΑΣΕΩΝ Ο υπολογισμός του αποτελέσματος μιάς έκφρασης εξαρτάται απο την προτεραιότητα και την συσχέτιση των τελεστών. Η προτεραιότητα καθορίζει τη σειρά εφαρμογής (order of application) των τελεστών. Για παράδειγμα, η έκφραση 2*2+4 υπολογίζεται με την τιμή 8 μιάς και ο τελεστής του Πολ/σμού έχει μεγαλύτερη προτεραιότητα από τον τελεστή της Πρόσθεσης. Χρησιμοποιώντας παρενθέσεις μπορούμε να αλλάξουμε τη δομή μιάς έκφρασης και ουσιαστικά να μεταβάλλουμε τον τρόπο υπολογισμού της. Για παράδειγμα η έκφραση 2*(2+4) υπολογίζεται με την τιμή 12. Η συσχέτιση καθορίζει τη σειρά εφαρμογής τελεστών με την ίδια προτεραιότητα. Για παράδειγμα θεωρώντας οτι οι τελεστές / και * έχουν την ίδια προτεραιότητα η συσχέτιση τους ορίζει ότι η έκφραση 6/2*3 υπολογίζεται με την τιμή 9, αντί τη τιμή 1.
ΠΡΟΤΕΡΑΙΟΤΗΤΑ ΚΑΙ ΣΥΣΧΕΤΙΣΗ ΤΕΛΕΣΤΩΝ ΤΕΛΕΣΤΗΣ ΣΥΣΧΕΤΙΣΗ ΥΨΗΛΗ Μοναδιαίος +, - Από δεξιά προς τα αριστερά ......... *, /, % Από αριστερά προς τα δεξιά ΧΑΜΗΛΗ Δυαδικός +, -
ΠΑΡΑΔΕΙΓΜΑΤΑ 1/5 υπολογίζεται με την τιμή 0 1/5 υπολογίζεται με την τιμή 0 10/3 υπολογίζεται με την τιμή 3 10%3 υπολογίζεται με την τιμή 1 10%2 υπολογίζεται με την τιμή 0 10+2 υπολογίζεται με την τιμη 12 10*2-2 υπολογίζεται με την τιμή 18 10/0 δεν ορίζεται (διαίρεση με 0) 10%0 δεν ορίζεται (υπόλοιπο διαίρεσης με 0) 7/2+3 υπολογίζεται με την τιμή 6 -10+2 υπολογίζεται με την τιμη -8 10/2 + -10/3 = (10/2) + ((-10)/3) υπολογίζεται με την τιμη 2
ΜΕΤΑΤΡΟΠΗ ΤΥΠΩΝ (1) Όταν οι μεταβλητές/σταθερές είναι του ιδίου τύπου το αποτέλεσμα είναι γενικά του αυτού τύπου. Όταν όμως οι μεταβλητές/σταθερές σε μία έκφραση δέν είναι του ιδίου τύπου τότε λέμε οτι έχουμε μία μικτή έκφραση. Στη C υπάρχουν μερικοί απλοί κανόνες αυτόματης μετατροπής τύπων στις μικτές εκφράσεις. Γενικά ο κανόνας είναι να μετατρέπεται ο τύπος με το μικρότερο μέγεθος στον τύπο με το μεγαλύτερο μέγεθος έτσι ώστε δεν χάνεται πληροφορία. Για παράδειγμα εαν f έιναι float και ο i είναι integer τότε στην έκφραση f+i o I μετατρέπεται σε float. Η έκφραση υπολογίζεται στον τύπο με το μεγαλύτερο μέγεθος Το αποτέλεσμ είναι τύπου όμοιου με τον «μεγαλύτερο» τύπο στην έκφαρση. Εκφράσεις που αναθέτουν ένα τύπο μεγαλύτερου μεγεθους σε ένα μικρότερο (π.χ. i = f) συνήθως δημιουργούν μια παρατήρηση (warning) από τον μεταφραστή (compiler) και γενικά πρέπει να αποφεύγονται.
ΠΙΝΑΚΑΣ ΜΕΤΑΤΡΟΠΗΣ ΤΥΠΩΝ Ο παρακάτω πίνακας δείχνει τον τύπο του αποτελέσματος σαν συνάρτηση των τύπων στην έκφραση. Μέγεθος Operand types int long float double long double
ΑΛΓΟΡΙΘΜΟΣ ΜΕΤΑΤΡΟΠΗΣ ΤΥΠΩΝ Έστω μια έκφραση x op y, για κάποιο τελεστή op. Βήμα 1. Κάθε char ή short μετατρέπεται σε int. Κάθε unsigned cha ή unsigned short μετατρέπεται σε unsigned (int). Βήμα 2. Αν μετά το πρώτο βήμα η έκφραση είναι μικτού τύπου η έκφραση έχει σαν αποτέλεσμα ένα τύπο ανάλογα με τον «μεγαλύτερο τύπο» που υπάρχει στην έκφραση και σύμφωνα με τον παρακάτω κανόνα μεγέθους τύπων int < unsigned < long < unsigned long < float < double
ΠΑΡΑΔΕΙΓΜΑΤΑ Έστω οι ακόλουθες δηλώσεις: char c; double d; float f; int i; long l; short s; unsigned u; Έκφραση Τελικός Τύπος c – s / i int u * 3 – i unsigned u * 3.0 – i double f * 3 – i float c + 1 int c + 1.0 float 3 * s + l long
ΛΟΓΙΚΟΙ ΤΕΛΕΣΤΕΣ και ΛΟΓΙΚΕΣ ΕΚΦΡΑΣΕΙΣ ΛΟΓΙΚΟΙ ΤΕΛΕΣΤΕΣ και ΛΟΓΙΚΕΣ ΕΚΦΡΑΣΕΙΣ Οι λογικές εκφράσεις παράγουν ένα από τα δύο αποτελέσματα: True, False Έχουμε τρεις βασικούς λογικούς τελεστές Σύζευξη (and) ορίζεται με τον τελεστή && στη C Διάζευξη (or) ορίζεται με τον τελεστή || στη C Άρνηση (not) ορίζεται με τον τελεστή ! στη C Οι λογικοί τελεστές χρησιμοποιούνται για να παράγουν λογικές εκφράσεις Παράδειγμα int a, b; a = 1; b = 0; (a && b) (a || b) ((!a || b) && c) Στη C η λογική τιμή true ορίζεται με την ακέραια τιμή 1 και η λογική τιμή false με την ακέραια τιμή 0
ΕΡΜΗΝΕΙΑ ΛΟΓΙΚΩΝ ΤΕΛΕΣΤΩΝ Έκφραση a b Αποτέλεσμα a && b True False a || b ! a
ΣΧΕΣΙΑΚΟΙ ΤΕΛΕΣΤΕΣ (relational operators) Οι βασικοί σχεσιακοί τελεστές είναι Τελεστής Ερμηνεία < Μικρότερο <= Μικρότερο ή ίσο > Μεγαλύτερο >= Μεγαλύτερο ή ίσο == Ίσο (έλεγχος) != Όχι ίσο (έλεγχος)
ΠΑΡΑΔΕΙΓΜA int main() { int a = 4; int b = 3; int c = 2; int x = (a < b); int y = (a == c); int z = ( x && y ) return (z); } Ποια είναι η τιμή της μεταβλητής z;
ΠΡΟΤΕΡΑΙΟΤΗΤΑ ΚΑΙ ΣΥΣΧΕΤΙΣΗ Όπως έχουμε δει ο υπολογισμός του αποτελέσματος μιας έκφρασης εξαρτάται από την προτεραιότητα και την συσχέτιση των τελεστών. Προτεραιότητα Τελεστής Συσχέτιση Υψηλή + - ++ ! -- (μοναδιαίος) Δεξιά προς αριστερά * / % Αριστερά προς δεξιά + - (δυαδικός) ΄΄ ΄΄ < <= > >= == != && || Χαμηλή = /= *= %= += -= Πως υπολογίζεται η έκφραση (1) α/ β*γ; και (2) η α == β+γ Απ. (1) Από αριστερά προς τα δεξιά (α / β) *γ και (2) με διαφορά προτεραιότητας (α == (β+γ))
ΕΛΕΓΧΟΣ ΡΟΗΣ ΠΡΟΓΡΑΜΜΑΤΟΣ Μέχρι τώρα έχουμε μιλήσει για στοιχεία της C που αφορούν τον ορισμό μεταβλητών, την είσοδο και εκτύπωση δεδομένων από το πληκτρολόγιο και την οθόνη αντίστοιχα, εκφράσεις και διάφορους τελεστές Ουσιαστικά στην απλούστερη μορφή ένα πρόγραμμα είναι μια ακολουθία από εντολές που εκτελούνται με τη σειρά (από την πρώτη εντολή προς την τελευταία) Όμως για να είμαστε σε θέση να υλοποιήσουμε πιο σύνθετους αλγόριθμους θα πρέπει να έχουμε την δυνατότητα του ελέγχου της ροής του προγράμματος Αυτό επιτυγχάνεται με εντολές ελέγχου ροής Διακλάδωση με συνθήκη Εντολές βρόγχου Συναρτήσεις
Εντολές Διακλάδωσης με Συνθήκη Η εντολή if χρησιμοποιείται για να ορίσει την υπό συνθήκη εκτέλεση μίας ή περισσοτέρων εντολών. Υπάρχουν δύο βασικοί τύποι της εντολής if 1. 2. Συνθήκη Συνθήκη True False True False Εντολές Εντολές Εντολές If-then-else If-then
Η ΠΡΩΤΗ ΜΟΡΦΗ Σύνταξη: if (<λογική έκφραση>) { εντολή1 εντολή2 ........... εντολήκ } Παράδειγμα if (a <= b) { a = a+1; b = b/3; } Εναλλακτικά if (a <= b) { a += 1; b /= 3; }
Η ΔΕΥΤΕΡΗ ΜΟΡΦΗ Παράδειγμα Σύνταξη: if (a <= b) { a=3; b=4; a = a+1; b = b/3; } else a = a+5; b = b%3; printf(“a is: %d\n”, a); Σύνταξη: if (<λογική έκφραση>) { εντολή1; εντολή2 ; ........... εντολήκ ; } else εντολήκ+1; εντολήκ+2; ............. εντολήν; Κάθε εντολή μπορεί να είναι σύνθετη (μπλοκ) { s1; s2; …. }
ΑΛΓΟΡΙΘΜΟΙ ΚΑΙ ΥΛΟΠΟΙΗΣΗ ΠΡΟΓΡΑΜΜΑΤΟΣ Έχουμε μιλήσει για τις δύο βασικές μεθόδους ανάλυσης ενός προβλήματος. Όμως πώς αυτές συσχετίζονται με την σχεδίαση προγραμμάτων; Η αναλυτική μέθοδος (top down) σχετίζεται με την μέθοδο σχεδίασης «διαίρει και βασίλευε» (divide and conquer), όπου για να σχεδιάσουμε ένα πρόγραμμα το διαιρούμε σε απλούστερα προγράμματα Η συνθετική μέθοδος (bottom up) σχετίζεται με την μέθοδο σχεδίασης «σταδιακή εξέλιξη» (successive refinement), όπου αρχίζουμε με ένα απλό πρόγραμμα και σταδιακά του προσθέτουμε περισσότερες λεπτομέρειες μέχρι που όλες οι περιπτώσεις που πρέπει να λυθούν έχουν λυθεί
ΨΕΥΔΟ-ΚΩΔΙΚΑΣ (pseudocode) Όταν είμαστε έτοιμοι να παρουσιάσουμε ένα αλγόριθμο (πρόγραμμα) μπορούμε να χρησιμοποιήσουμε ψευδοκώδικα αντί μια γλώσσα προγραμματισμού Αυτό μας δίνει το πλεονέκτημα ότι η σχεδίαση του αλγόριθμου δεν επηρεάζεται από την γλώσσα που θα χρησιμοποιηθεί οπότε διαχωρίζονται οι φάσεις σχεδίασης προγράμματος (design) από την υλοποίηση του προγράμματος (implementation) Ο ψευδοκώδικας είναι ένα μαθηματικό μοντέλο του αλγόριθμου, εκφραζόμενο σε μια μορφή που θυμίζει (αλλά δεν είναι) γλώσσα προγραμματισμού Η ιδέα είναι ότι από τον ψευδοκώδικα είναι απλή η διαδικασία της υλοποίησης (αναλογία μεταξύ Αρχιτέκτονα που σχεδιάζει και Εργολάβου που ακολουθεί πιστά το σχέδιο για την ανέγερση μιας οικοδομής)
ΠΑΡΑΔΕΙΓΜΑ ΨΕΥΔΟΚΩΔΙΚΑ Μία πιθανή μορφή ψευδοκώδικα είναι: let name1, name2 (ορισμός μεταβλητών) get name1, name2 (διάβασε τις μεταβλητές name1, name2 από κάποια μονάδα εισόδου) record name1, name2 (αποθήκευσε τις τρέχουσες τιμές των μεταβλητών name1, name2 σε κάποια εξωτερική μονάδα – π.χ. Δίσκο) name1 έκφραση (αποθήκευσε την τιμή της έκφρασης στη μεταβλητή name1 if expression then A else B endif
ΠΑΡΑΔΕΙΓΜΑ Πρόβλημα Να σχεδιάσετε και να υλοποιήσετε ένα πρόγραμμα το οποίο να διαβάζει τις παραμέτρους ενός πολυωνύμου δευτέρου βαθμού αx2 + βx + γ = Π(x) και να υπολογίζει τις ρίζες τις εξίσωσης Π(x) = 0. Ο αλγόριθμος είναι x = (- β ± sqrt(β2 – 4*α*γ))/ (2*α)
ΠΑΡΑΔΕΙΓΜΑ Μία απλή μορφή του ψευδοκώδικα είναι (περίπτωση με πραγματικές ρίζες) get a, b, c; root1 (-b + sqrt(b2 -4ac)/(2a); root2 (-b - sqrt(b2 -4ac)/(2a); record root1, root2; Όμως τι γίνεται για τις άλλες περιπτώσεις όπου α = 0, ή α = 0 και β = 0, ή η διακρίνουσα είναι αρνητική;
ΣΤΑΔΙΑΚΗ ΕΞΕΛΙΞΗ ΤΟΥ ΨΕΥΔΟΚΩΔΙΚΑ Ο ψευδοκώδικας γίνεται get a, b, c; if a = 0 then if b = 0 then case 1; record case; else case 2; root1 - c/b record case, root1; endif if b2 >= 4ac then case 3; root1 (-b + sqrt(b2 – 4ac) / (2a); root2 (-b - sqrt(b2 – 4ac) / (2a); record case, root1, root2; case 4; root1 (-b/ (2a)) + j (sqrt(abs(b2 – 4ac)) / (2a)); root1 (-b/ (2a)) - j (sqrt(abs(b2 – 4ac)) / (2a)); Η υλοποίηση του προγράμματος είναι τώρα απλή υπόθεση.
ΕΝΤΟΛΕΣ ΕΠΑΝΑΛΗΨΗΣ (ΒΡΟΓΧΟΥ) Οι εντολές βρόγχου (iterative statements) στη C επιτρέπουν την επαναληπτική εκτέλεση μιάς σειράς εντολών όσο κάποιες λογικές συνθήκες ισχύουν. Στη C έχουμε τις παρακάτω εντολές βρόγχου, που αντιστοιχούν και σε διαφορετικές περιπτώσεις επανάληψης: Εντολή while Εντολή do-while Εντολή for Η σειρά των εντολών που επαναλαμβάνεται ονομάζεται το «σώμα» (body) της εντολής βρόγχου. Η λογική συνθήκη ονομάζεται «συνθήκη» (condition). Το σημείο που η επανάληψη σταματά ονομάζεται «σταθερό σημείο» (fixed point)
ΚΑΤΑΣΚΕΥΕΣ ΕΠΑΝΑΛΗΨΗΣ Πρίν προχωρήσουμε όμως στη μελέτη της σύνταξης αυτών των εντολών ας μελετήσουμε πρωτα την κατασκευή τους και γιατι όχι τον αντίστοιχο ψευδοκώδικα τους και την γραφική ερμηνεία τους Κατασκευή (structure) δίνει τη γενική ιδέα και την ερμηνεία μέσω ενός μοντέλου (π.χ. Διαγράμματος ροής). Η κατασκευή είναι σχετικά ανεξαρτητη της γλώσσας προγραμματισμού. Η εντολή (statement ή command) έχει να κάνει με την συγκεκριμένη γλώσσα προγραμματισμου (π.χ. Σύνταξη της C) και υλοποιεί μία κατασκευή. Στη C έχουμε τρείς κατασκευέ βρογχου ή επανάληψης (iterative structures). Δύο απο αυτές υλοποιούνται άμεσα με εντολές της C
ΚΑΤΑΣΚΕΥΕΣ ΕΠΑΝΑΛΗΨΗΣ 1 while A do B endwhile Όσο ισχύει η (λογική έκφραση Α) επανελαβε σειρά εντολών Β Τελος επανάληψης Συνθήκη Β Α True False
ΚΑΤΑΣΚΕΥΕΣ ΕΠΑΝΑΛΗΨΗΣ 2 do B while A επανελαβε σειρά εντολών Β όσο ισχύει η (λογική έκφραση Α) Τελος επανάληψης Συνθήκη Β Α True False Η διαφορά απο την προηγούμενη κατασκευή είναι οτι η σειρά εντολών Β θα εκτελεστεί τουλαχιστον μία φορά πρίν τη λογίκή συνθήκη Α
ΕΝΤΟΛΗ while Η εντολή while έχει τη μορφή σειρά εντολών Β Παράδειγμα: int i = 0 while (i <= 10) { printf(“The number is: %d\n”, i); i++; } Σημαίνει i = i+1
ΠΑΡΑΔΕΙΓΜΑ Ένα μικρό πρόγραμμα που υπολογίζει το άθροισμα των αριθμών από 1 μέχρι 10. #include <stdio.h> int main() { int i = 1; int sum = 0; while (i <= 10) { sum = sum + i; i = i+1; } printf(“The sum of numbers from 1 to 10 is: %d\n”, sum); return (sum);
ΠΑΡΑΔΕΙΓΜΑ Ένα μικρό πρόγραμμα που υπολογίζει το άθροισμα των κύβων των αριθμών 0.4 0.8 1.2 1.6 2.0 2.4 #include <stdio.h> float main() { float sum = 0.0; float increment = 0.4; float x = 0.4; while (x <= 2.5) { sum = sum + x*x*x; x = x+ increment; } printf(“The sum of the cubes is: %f\n”, sum); return (sum); Λίγο μεγαλύτερη τιμή από 2.4 για να προλάβουμε προβλήματα με rounding error π.χ. Στη τελευταία επανάληψη η μεταβλητή χ να είναι λίγο μεγαλύτερη του 2.4 μιας και οι float μπορεί να έχουν περισσότερα από ένα δεκαδικό
ΠΑΡΑΔΕΙΓΜΑ Ένα μικρό πρόγραμμα που διαβάζει από μία πηγή εισόδου (input stream) χαρακτήρες (characters), και τους τυπώνει σε πηγή εξόδου (output stream). #include <stdio.h> float main() { int c; int nl = 0; ………… π.χ. εντολές γιά άνοιγμα αρχείου για διάβασμα .............. c = getchar(); while (c != EOF) { putchar(c); if(c == ‘\n’) nl++; c= getchar(); }
ΕΝΤΟΛΗ do while Η εντολή while έχει τη μορφή do σειρά εντολών Β Χρησιμοποιείται όταν θέλουμε η σειρά εντολών Β να εκτελεστεί τουλάχιστον μία φορά πριν τον έλεγχο τερματισμού. Παράδειγμα: #include <stdio.h> int i = 0 do { printf(“The number is: %d\n”, i); i = i + 1; } while (i <= 10)
ΠΑΡΑΔΕΙΓΜΑ Ένα μικρό πρόγραμμα που διαβάζει αριθμούς από τον χρήστη και υπολογίζει το άθροισμα τους. Το πρόγραμμα θα σταματήσει όταν ο χρήστης δώσει ένα αριθμό μικρότερο ή ίσο του μηδενός. Εάν το άθροισμα είναι αρνητικό τότε επιστρέφουμε μηδέν. #include <stdio.h> float main() { signed float number; signed float sum = 0; do { printf(“Enter a number: “); scanf(“%f”, &number); sum = sum + number; printf(“\n”); /* Πήγαινε στην επόμενη γραμμή στην οθόνη */ } while (!(number == 0 || number < 0)) if( sum <= 0) /* Στη περίπτωση που ο χρήστης εισάγει 0 ή αρνητικό σαν πρώτο αριθμό */ sum = 0; /* θέσε τη μεταβλητή sum ίση πάλι με το μηδέν */ printf(“The sum of numbers is: %f\n”, sum); return (sum);
ΕΝΤΟΛΗ for Παράδειγμα: Η εντολή for έχει τη μορφή σειρά εντολών Β Παράδειγμα: #include <stdio.h> int main () { int i, sum=0; for (i = 1; i <= 10; i++) { sum = sum + 1; printf(The sum now is: %d\n”, sum); } printf(“The final sum is:%d\n”, sum); return (sum);
ΠΑΡΑΔΕΙΓΜΑ #include <stdio.h> int main() { int number; 100 Ένα μικρό πρόγραμμα που υπολογίζει το άθροισμα ∑ χ3 #include <stdio.h> int main() { int number; int sum = 0; for (number = 10; number <= 100; number++) { sum = sum + number * number * number; printf(“The number is %d and the sum is now: %d\n”, number, sum); } printf(“The final sum is %d\n”, sum); return (sum); Χ=10
ΠΑΡΑΔΕΙΓΜΑ Ένα μικρό πρόγραμμα που υπολογίζει το άθροισμα ∑ χ3 #include <stdio.h> int main() { int sum; int number; for (sum = 0, number = 10; number <= 100; number++) { sum = sum + number * number * number; printf(“The number is %d and the sum is now: %d\n”, number, sum); } printf(“The final sum is %d\n”, sum); return (sum); 100 Χ=10 Ο Τελεστής ,
ΠΑΡΑΔΕΙΓΜΑ Ένα μικρό πρόγραμμα που να υπολογίζει τα τετράγωνα αριθμών απο το 1 μεχρι n #include <stdio.h> int main() { int i, n; printf(“Enter the max number to compute the square: “); scanf(“%d”, &n); for (i = 1; i <= n; i++) { printf(“%10d%10d\n”, i, i*i); } return (1); 1 4 9 16 ……….
ΙΣΟΔΥΝΑΜΙΕΣ ΕΝΤΟΛΩΝ while, for <αρχική τιμή 1> while (<έκφραση τερματισμού 2>) { εντολές 4 <έκφραση αναπροσδιορισμού τιμών 3> } i = 1; while (i <= 10) statements; i++; for (<αρχική τιμή 1>; <έκφραση τερματισμού 2>; <έκφραση αναπροσδιορισμού τιμών 3>) { εντολές 4 } for (i = 1; i <= 10; i++) statements
ΟΡΙΣΜΟΙ ΜΕΤΑΒΛΗΤΩΝ ΒΡΟΓΧΟΥ - 1 Ας θεωρήσουμε το πρόγραμμα for (int i = 1; i <= 100; i++) printf(“The number is %d\n”, i); Η μεταβλητή i δεν υπάρχει και δεν είναι διαθέσιμη μετά την εκτέλεση της εντολής for Αντίθετα στο πρόγραμμα int i; for(i = 1; i<=100; i++) Η μεταβλητή i είναι διαθέσιμη μετά την εκτέλεση της εντολής for. Μερικοί μεταφραστές όμως δεν θεωρούν τη μεταβλητή i διαθέσιμη !!!
ΟΡΙΣΜΟΙ ΜΕΤΑΒΛΗΤΩΝ ΒΡΟΓΧΟΥ - 2 Ας θεωρήσουμε το πρόγραμμα void main() { do { float i = 10.0 statements; ………….. i++; } while i <= 100.0 Η μεταβλητή i δεν μπορεί να ορισθεί μέσα σ’ένα εσωτερικό block εντολής. Το πρόγραμμα αυτό είναι λάθος.
ΟΡΙΣΜΟΙ ΜΕΤΑΒΛΗΤΩΝ ΒΡΟΓΧΟΥ - 3 Αντίθετα sτο πρόγραμμα void main() { float i = 10.0 do { statements; ………….. i++; } while i <= 100.0 Η μεταβλητή i έχει ορισθεί στο block του main. Θα μπορούσε να είχε ορισθεί και μετά τις εντολές του προεπεξεργαστή. Θα μιλήσουμε για το εύρος και ορίζοντα των δηλώσεςν των μεταβλητών αργότερα.
ΚΑΤΑΣΚΕΥΗ ΕΠΑΝΑΛΗΨΗΣ 3 Σε μερικές περιπτώσεις μας ενδιαφέρει να έχουμε την λογική έκφραση τερματισμού του βρογχου ούτε στην αρχή, ούτε στο τέλος, αλλά κάπου στη μέση της σειράς εντολών που εκτελούνται επαναληπτικά Δυστυχώς δεν υπάρχει άμεση εντολή στη C που να υλοποιεί αυτή τη περίπτωση (π.χ. Η κατασκευη 1 υλοποιείται με την εντολή while και for ενώ η κατασκευή 2 με την εντολή do-while) Γραφικά η κατασκευή 3 είναι:
ΚΑΤΑΣΚΕΥΕΣ ΕΠΑΝΑΛΗΨΗΣ 3 – ΓΡΑΦΙΚΗ ΠΑΡΑΣΤΑΣΗ Συνθήκη Β2 Α True False Β1 Η διαφορά από την προηγούμενες κατασκευές είναι ότι η σειρά εντολών Β1 θα εκτελεστεί τουλάχιστον μία φορά πριν τη λογική συνθήκη Α η οποία εάν είναι αληθής τότε η επανάληψη σταματά, αλλιώς η σειρά εντολών Β2 θα εκτελεστεί, και ο βρόγχος θα επαναληφθεί.
ΥΛΟΠΟΙΗΣΗ ΤΗΣ ΚΑΤΑΣΚΕΥΗΣ 3 #define TRUE 1 #define FALSE 0 void main() { int flag; flag = TRUE; while (flag) { εντολές Β1; if (λογική συνθήκη Α) flag = FALSE; } else εντολές Β2; Η υλοποίηση αυτή απαιτεί μια παραπάνω μεταβλητή (flag) αλλά είναι εύκολα κατανοητή και επεκτάσιμη
ΥΛΟΠΟΙΗΣΗ ΤΗΣ ΚΑΤΑΣΚΕΥΗΣ 3 #define TRUE 1 void main() { while (TRUE) /* Συνεχής επανάληψη */ { εντολές Β1; if (λογική συνθήκη Α) break; εντολές Β2; } Αυτό το loop δεν θα σταμάταγε ποτέ Μόνο η πρώτη εντολή break εκτελείται στο if διότι δεν υπάρχουν¨{ }. Η εντολή break σταματά το βρόγχο και μεταφέρει τον έλεγχο στην πρώτη εντολή μετά το βρογχο.
ΛΙΓΑ ΛΟΓΙΑ ΓΙΑ ΤΗΝ ΚΑΤΑΣΚΕΥΗ 3 Φαίνεται και είναι λίγο παράξενη. Όμως την συναντάμε συχνά ιδιαίτερα όταν διαβάζουμε από ένα αρχείο ή ΄το πρόγραμμα λαμβάνει στοιχεία από τον χρήστη. Η πρώτη υλοποίηση χρειάζεται μία επιπλέον μεταβλητή αλλά είναι εύκολα κατανοητή. Η δεύτερη υλοποίηση χρησιμοποιεί την εντολή break που χρησιμοποιείται γιά να τερματίσει οποιοδήποτε βρόγχο. Όμως η εντολή break μπορεί να δημιουργήσει σύγχυση ιδιαίτερα όταν υπάρχουν βρόγχοι μέσα σε άλλους βρόγχους. Παρακάτω παρουσιάζονται τέσσερα σενάρια χρήσης της κατασκευής 3 (βιβιλογραφία: B. Preiss, J, Fields)
ΣΕΝΑΡΙΟ 1 void main() { int done = 0; while (! done) { διαβάζουμε στοιχεία; } if (συνθήκη τερματισμού εισόδου στοιχείων) done = 1; else διαδικασίες χρήσης των στοιχείων Η υλοποίηση αυτή απαιτεί μια παραπάνω μεταβλητή (done) αλλά είναι εύκολα κατανοητή και επεκτάσιμη
ΣΕΝΑΡΙΟ 2 #define TRUE 1 void main() { while (TRUE) { Η break δημιουργεί τα γνωστά προβλήματα κατανόησης. Μπορεί όμως σ’αυτή τη περίπτωση να αντικατασταθεί με την εντολή return, και όλη η υλοποίηση να γίνει μια function που κάπως θα επιστρέφει τα επιθυμητά αποτελέσματα #define TRUE 1 void main() { while (TRUE) { διαβάζουμε στοιχεία; if (συνθήκη τερματισμού εισόδου στοιχείων) break; διαδικασίες χρήσης των στοιχείων }
ΣΕΝΑΡΙΟ 3 Η υλοποίηση αυτή είναι παρόμοια με αυτή του Σεναρίου 1, αλλά χρησιμοποιεί την εντολή do-while void main() { int done = 0; do { διαβάζουμε στοιχεία; if (συνθήκη τερματισμού εισόδου στοιχείων) done = 1; else διαδικασίες χρήσης των στοιχείων } while (!done)
ΣΕΝΑΡΙΟ 4 Στην υλοποίηση αυτή θεωρούμε ότι το διάβασμα των στοιχείων γίνεται από μία συνάρτηση (function) η οποία επιστρέφει κάποια αριθμητική τιμή (ώστε να μπορεί να χρησιμοποιηθεί σε μιά λογική έκφραση, ΚΑΙ έχει σαν παράπλευρο αποτέλεσμα την απόθεση των στοιχείων σε κάποιες μεταβλητές που είναι ορατές από το πρόγραμμα. Γενικά όχι καλός τρόπος. void main() { ................. while (διαβάζουμε στοιχεία() && ! συνθήκη τερματισμού εισόδου στοιχείων) { διαδικασίες χρήσης των στοιχείων }
ΣΥΝΘΕΤΕΣ ΕΝΤΟΛΕΣ ΒΡΟΓΧΟΥ Σε μερικές περιπτώσεις χρειαζόμαστε να έχουμε βρόγχους (loops) μέσα σε άλλους βρόγχους (nested loops) Γενικά σ’αυτή τη περίπτωση το ζητούμενο έιναι να προσέχουμε ποιές διαδικασίες χρειάζονται σε ποιό βρόγχο οπότε να μην έχουμε παράπλευρα φαινόμενα, ή παραπάνω απ΄ότι χρειάζεται πολυπλοκότητα
ΠΑΡΑΔΕΙΓΜΑ Ας υλοποιήσουμε ένα πρόγραμμα το οποίο θα υπολογίζει το αθροισμα ∑ ∑ κ*χ #include <stdio.h> int main() { int k, x, sum = 0; for(k=1; k<= 5; k++) for(x=2; x<=10; x++ sum = sum + k*x; return (sum) } 5 10 k=1 x=2 (1*2 + 1*3 + … 1*10) + (2*2 + 2*3 + ….2*10) + ……………………….. (5*2 + 5*3 + …5*10)
ΕΙΣΑΓΩΓΗ ΣΤΑ ΑΡΧΕΙΑ (FILES) Στη C ο όρος stream ορίζει μία πηγή στοιχείων εισόδου, ή ένα προορισμό για στοιχεία εξόδου Δύο βασικά και οριζόμενα αυτόματα απο τη C streams είναι το πληκρολόγιo (keyboard) για είσοδο (input), και η οθόνη (screen or terminal), για έξοδο (output) Τα μεγάλα προγράμματα μπορούν να έχουν πολλές πηγές εισόδου π.χ. Modems, CD drives, files, κλπ. Τα προγράμματα συνήθως διαβάζουν στοιχεία απο μια πηγή και στέλνουν αποτελέσματα σε ένα προορισμό (που μπορεί να είναι πηγή στοιχείων για κάποιο άλλο πρόγραμμα)
ΔΕΙΚΤΕΣ ΑΡΧΕΙΩΝ (FILE POINTERS) Ένα C πρόγραμμα μπορεί να χρησιμοποιήσει ένα αρχείο σαν πηγή στοιχείων εισόδου, ή προορισμό στοιχείων εξόδου με το ορισμό δεικτών αρχείου Ο ορισμός ενός δείκτη αρχείου γίνεται με την εντολή FILE * Ουσιαστικά ένας δείκτης αρχείου ορίζει οτι μια μεταβλητή (π.χ. ifp) είναι τύπου (type) FILE, και το πρόγραμμα έτσι καταλαβαίνει πως να χρησιμοποιήσει τη μεταβλητή ifp. Για παράδειγμα έαν θέλουμε να έχουμε δύο αρχέια για χρήση (είσοδο, ή έξοδο) στο πρόγραμμά μας τα οποία χρησιμοποιούνται και ορίζονται με τις μεταβλητές π.χ. fp1, fp2 με τότε η εντολή ορισμoύ τους είναι: FILE *fp1, *fp2;
ΒΑΣΙΚΕΣ ΠΗΓΕΣ ΔΕΙΚΤΗΣ ΑΡΧΕΙΟΥ ΠΗΓΗ ΕΡΜΗΝΕΙΑ stdin Βασική πηγή εισόδου Πληκτρολόγιο stdout Βασική πηγή εξόδου Οθόνη stderr Βασική πηγή διαγνωστικών μηνυμάτων Οι εντολές printf, scanf που έχουμε εξετάσει μέχρι τώρα λαμβάνουν στοιχεία από την πηγή stdin και αποστέλουν στοιχεία στο stdout. Μπορύμε να αλλάξουμε την ερμηνεία των stdin, και stdout να είναι ένα αρχείο αντί του πληκτρολογίου ή της οθόνης αντίστοιχα. Αυτό γίνεται με τη χρήση του λειτουργικού συστήματος και των τελεστών > ή < π.χ. demo < in.dat Αρχείο με στοιχεία εισόδου Πρόγραμμα
ΨΗΦΙΑΚΑ ΑΡΧΕΙΑ ΚΑΙ ΑΡΧΕΙΑ ΚΕΙΜΕΝΟΥ ΨΗΦΙΑΚΑ ΑΡΧΕΙΑ ΚΑΙ ΑΡΧΕΙΑ ΚΕΙΜΕΝΟΥ Η βιβλιοθήκη stdio.h υποστηρίζει δύο ειδών αρχεία – ψηφιακά, και κειμένου (χαρακτήρων). Ο πηγαίος κώδικας που γράφουμε για παράδειγμα αποθηκεύεται σ’ένα αρχείο, ενώ το συμβολομεταφρασμένο πρόγραμμα σ’ένα ψηφικό αρχείο (θα το δείτε αυτο εάν προσπαθήσετε να ανοίξετε μ’ένα επεξεργαστή κειμένου ή editor ένα exe αρχείο. Στη συγκεκριμένο παράδειγμα ο μεταφραστής είναι ένα πρόγραμμα (ίσως και αυτό γραμμένο στη C !!) το οποίο έχει ώς πηγή εισόδου ένα αρχείο κειμένου (πηγαίο κώδικα), και σαν προόρισμό ένα αρχείο κειμένου (assembly code). O συμβολομεταφραστής είναι και αυτός ένα πρόγραμμα (ίσως και αυτό γραμμένο στη C !!) το οποίο έχει ώς πηγή εισόδου ένα αρχείο κειμένου (assembly code) και σαν προόρισμό ένα ψηφιακό αρχείο (binary code).
ΨΗΦΙΑΚΑ ΑΡΧΕΙΑ ΚΑΙ ΑΡΧΕΙΑ ΧΑΡΑΚΤΗΡΩΝ - ΠΑΡΑΔΕΙΓΜΑ Για παράδειγμα άς δούμε πως ο αριθμός 32767 θα μπορούσε να παρασταθεί σ΄ένα αρχέιο κειμένου, και σ΄ένα ψηφιακό αρχείο. Σ΄ένα αρχείο κειμένου θα φαινεται σαν η «λεξη» κειμένου 32767 που διαβάζεται απο τόν χρήστη. Εσωτερικά όμως στη μνήμη του Η/Υ θα παριστάνεται σαν η ακολουθία (έαν υποθέσουμε ότι χρησιμοποίούμε κώδικα ASCII (συνολο 5 bytes, 8 bits = 1 byte) 00110011 00110010 00110111 00110110 3 2 2 2 7 Ο ίδιος αριθμός 32767 θα μπορούσε να παρασταθεί σ΄ένα ψηφιακό αρχέιο σαν δύο bytes 01111111 11111111
ΨΗΦΙΑΚΑ ΑΡΧΕΙΑ ΚΑΙ ΑΡΧΕΙΑ KEIMENOY - 2 Γιατί όμως χρειαζόμαστε δύο τύπων αρχεία? Μερικοί από τους λόγους είναι Μέγεθος Ο τρόπος που το κάθε λειτουργικό σύστημα καταλαβαίνει τη δομή του αρχείου (EOL, EOF) Η δυνατότητα χρήσης ειδικών χαρακτήρων Τα αρχεία κειμένου συνήθως αποτελούνται από σειρές γραμμών, και κάθε γραμμή σταματά με ένα ειδικό χαρακτήρα (end of line). Επείσης υπάρχει ένας ειδικός χαρακτήρας για να δηλώνει το τέλος του αρχείου (end of file). Τα ψηφιακά αρχεία συνήθως δεν είναι χωρισμένα σε γραμμές, και επειδή ένα ψηφιακό αρχείο μπορεί να περιέχει οποιοδήποτε στοιχείο δεν μπορούμε συνήθως να διακρίνουμε το τέλος μιάς «γραμμής»
ΨΗΦΙΑΚΑ ΑΡΧΕΙΑ ΚΑΙ ΑΡΧΕΙΑ KEIMENOY (DOS) End of line: Ο τρόπος που ορίζεται το τέλος μιάς γραμμής σ΄ένα αρχείο κειμένου είναι ουσιαστικά δύο χαρακτήρες – τέλος γραμμής (carriage return) και νέα γραμμή (line feed). Ο τρόπος για ένα ψηφιακό αρχείο είναι ένας χαρακτήρας - νέα γραμμή (line feed). End of file: Ο χαρακτήρας Control-Z \x1a σ΄ένα αρχείο κειμένου ορίζει το τέλος του αρχείου. Σ’ένα ψηφιακό αρχείο ο χαρακτήρας Control-Z δέν έχει σημασία. Στο UNIX τα ψηφιακά αρχεία και τα αρχεία κειμένου έχουν την ίδι δομή σεχετικα με το End of line, End of file.
ΛΕΙΤΟΥΡΓΙΕΣ ΑΡΧΕΙΩΝ Ανοιγμα αρχείου: Για να «ανοίξουμε ένα αρχείο» σαν πηγή εισόδου ή εξόδου, χρησιμοποίούμε την εντολή fopen. H γενική σύνταξη είναι fopen(<ονομα αρχείου>, <χρήση>) Παράδειγμα fopen(“c:\\project\\test.dat”, “r”)
fopen Η εντολή (ουσιαστικά συνάρτηση) fopen, επιστρέφει ένα δείκτη αρχείου. Επιστρέγει NULL εάν το αρχείο δεν μπορεί να ανοίξει. Οπότε η ολοκληρωμένη εικόνα είναι: FILE *fp; ……….. fp = fopen(“c:\\project\\test.dat”, “r”) Δήλωση δέικτη Χρήση για είσοδο (read) Για να μην θεωρήσει ο μεταφραστής ότι είναι ειδικοί χαρακτήρες
ΠΑΡΑΔΕΙΓΜΑ Ένα μικρό πρόγραμμα που να ανοίγει ένα αρχείο #include <stdio.h> FILE *fp; int main() { fp = fopen(“test.dat”, “r”) ……………. }
fclose Η εντολή κλείνει ένα αρχείο στο τέλος του προγράμματος ή όταν αυτό δεν χρειάζεται πιά είναι η fclose. H σύνταξη της εντολής fclose είναι fclose (<δείκτης αρχείου>) Παράδειγμα FILE *fp; ……….. fp = fopen(“c:\\project\\test.dat”, “r”) ............. fclose(fp)
ΠΑΡΑΔΕΙΓΜΑ Ένα μικρό πρόγραμμα που να ανοίγει ένα αρχείο να ελέγχει εάν μπορεί να ανοίξει και να το κλείνει. #include <stdio.h> #include <stdlib.h> #define FILE_NAME “test.dat” int main() { FILE *fp; fp = fopen(“test.dat”, “r”); if (fp == NULL) { printf(“Can not open file %s\n”, FILE_NAME); exit(EXIT_FAILURE); } ……………….. fclose(fp);
ΚΩΔΙΚΟΙ ΧΡΗΣΗΣ ΚΩΔΙΚΟΣ ΕΡΜΗΝΕΙΑ “r” Άνοιγμα για να διαβάσει το πρόγραμμα στοιχεία “w” Άνοιγμα για να «γραψει» το πρόγραμμα αποτελέσματα “a” Άνοιγμα για να «γράψει» στο τέλος υπάρχοντος αρχείου “r+” Άνοιγμα για είσοδο-έξοδο, απο την αρχή του αρχείου “w+” Άνοιγμα για είσοδο-έξοδο, με κάλυψη “a+” Άνοιγμα για είσοδο-έξοδο, απο το τέλος εάν το αρχείο υπάρχει. Είδαμε ότι για να ανοίξουμε ένα αρχέιο χρησιμοποιούμε την εντολη fopen με σύνταξη fopen(<ονομα αρχείου>, <χρήση>) Οι κωδικοι χρήσης είναι:
ΕΙΣΟΔΟΣ/ΕΞΟΔΟΣ ΣΤOΙΧΕΙΩΝ ΑΠΟ ΑΡΧΕΙΟ Η είσοδος/έξοδος στοιχείων απο αρχείο είναι πανεύκολη ! Χρησιμοποιεί τις εντολές fprintf, fscanf που έχουν λιγο πολύ πανομοιότυπη σύνταξη με τις printf και, scanf !! Η σύνταξη είναι: fprintf( <δείκτης αρχείου> , <δήλωση φόρμας>, έκφραση1, έκφραση2 ...) fscanf ( <δείκτης αρχείου> , <δήλωση φόρμας>, έκφραση1, έκφραση2 ...)
ΠΑΡΑΔΕΙΓΜΑ Το πρόγραμμα διαβάζει χαρακτήρες από #include <stdio.h> Int main(){ FILE *ifp; FILE *ofp; int c; ifp = fopen(“test.dat”, “r”); ofp = fopen(out.dat”, “w”); if(ifp == NULL) { printf(“Can not open input file\n”); return(0); } if(ofp == NULL) { printf(“Can not open output file\n); return (1); c = getc(ifp); while(c != EOF) { putc(c, ofp); fclose(ifp); fclose(ofp); return(2) Το πρόγραμμα διαβάζει χαρακτήρες από το αρχείο ifp (“test.dat”) και τους αντιγράφει στο αρχείο ofp (“out.dat”)
ΠΑΡΑΔΕΙΓΜΑ Το πρόγραμμα διαβάζει ακεραίους από #include <stdio.h> int main(){ FILE *ifp; FILE *ofp; int c; ifp = fopen(“test.dat”, “r”); ofp = fopen(out.dat”, “w”); if(ifp == NULL) { printf(“Can not open input file\n”); return(0); } if(ofp == NULL) { printf(“Can not open output file\n); return (1); fscanf(ifp, “%d\n”, &c); while(c != EOF) { fprintf(ofp, “%d\n”, c); fclose(ifp); fclose(ofp); return(2) Το πρόγραμμα διαβάζει ακεραίους από το αρχείο ifp (“test.dat”) και τους αντιγράφει στο αρχείο ofp (“out.dat”)