Περιεχόμενα 1 Εισαγωγή 31 1.1 Μια σύντομη ιστορική αναδρομή των υπολογιστών... 32 1.2 Τι είναι η επιστήμη των υπολογιστών;... 36 1.3 Μια σύντομη περιήγηση στο υλικό των υπολογιστών... 38 Κεντρική μονάδα επεξεργασίας (CPU) 39, Μνήμη 39, Δευτερεύουσα αποθήκευση 39, Συσκευές εισόδου/εξόδου (I/O) 40 1.4 Αλγόριθμοι... 41 1.5 Γλώσσες προγραμματισμού και μεταγλώττιση... 42 1.6 Λάθη προγραμματισμού και αποσφαλμάτωση... 45 1.7 Συντήρηση λογισμικού... 46 1.8 Η σημασία της τεχνολογίας λογισμικού... 47 1.9 Μερικές σκέψεις για τη γλώσσα προγραμματισμού C... 48 Σύνοψη...49 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...50 ΜΕΡΟΣ ΠΡΩΤΟ Βασικές έννοιες της γλώσσας προγραμματισμού C...51 2 Εκμάθηση με παραδείγματα 53 2.1 Το πρόγραμμα "Hello world"... 54 Σχόλια 55, Συμπεριλήψεις βιβλιοθηκών 56, Το κυρίως πρόγραμμα 57 2.2 Ένα πρόγραμμα άθροισης δύο αριθμών... 59 Η φάση εισόδου 61, Η φάση υπολογισμού 63, Η φάση εξόδου 63 2.3 Απόψεις σχετικές με τη διαδικασία του προγραμματισμού... 65 2.4 Τύποι δεδομένων... 66 Δεδομένα κινητής υποδιαστολής 67, Αλφαριθμητικά δεδομένα 68 2.5 Παραστάσεις... 70 Σταθερές 71, Μεταβλητές 72, Εντολές ανάθεσης 75, Τελεστές και τελεστέοι 77, Συνδυασμός ακεραίων και αριθμών κινητής υποδιαστολής 78, Διαίρεση ακεραίων και ο τελεστής υπολοίπου 79, Προτεραιότητα 80, Εφαρμογή των κανόνων προτεραιότητας 82, Μετατροπή τύπων 83 Σύνοψη...86 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...87 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...90 21
22 Περιεχόμενα 3 Επίλυση προβλημάτων 93 3.1 Ιδιωματισμοί και υποδείγματα προγραμματισμού... 94 Ιδιωματισμοί σύντομης ανάθεσης 95, Τελεστές αύξησης και μείωσης 97 3.2 Επίλυση προβλημάτων σε μεγαλύτερη κλίμακα... 98 3.3 Εντολές ελέγχου... 99 Ο ιδιωματισμός επανάληψης Ν φορές 100, Επανάληψη και βρόχοι 101, Μεταβλητές αριθμοδείκτη 102, Η σπουδαιότητα της ανάθεσης αρχικών τιμών 104, Ο ιδιωματισμός ανάγνωσης έως φρουρό 105, Δόμηση μιας πρακτικότερης εφαρμογής 107, Εκτέλεση υπό συνθήκη και η εντολή if 110 3.4 Μια άσκηση αποσφαλμάτωσης... 113 3.5 Μορφοποιημένη έξοδος... 117 Κωδικοί μορφοποίησης για τη συνάρτηση printf 119, Ρύθμιση διαστημάτων, στοίχισης, και ακρίβειας 120 3.6 Δόμηση ενός προγράμματος... 124 Στυλ προγραμματισμού 124, Πρόβλεψη για αλλαγές 126, Ο μηχανισμός #define 127 Σύνοψη...129 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...130 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...132 4 Μορφές εντολών 137 4.1 Απλές εντολές... 138 Ενσωματωμένες αναθέσεις τιμών 140, Πολλαπλές αναθέσεις 140, Μπλοκ κώδικα 142 4.2 Εντολές ελέγχου... 143 4.3 Λογικά δεδομένα... 144 Σχεσιακοί τελεστές 144, Λογικοί τελεστές 146, Πρόωρη αποτίμηση 148, Σημαίες 149, Αποφυγή πλεονασμού στις λογικές παραστάσεις 150, Ένα παράδειγμα υπολογισμού λογικής τιμής 151 4.4 Οι εντολές if... 152 Εντολές if μίας γραμμής 154, Εντολές if πολλών γραμμών 154, Η εντολή ifelse 155, Εντολές if σε μορφή στοίβας 155, Ο τελεστής?: (προαιρετικό) 156 4.5 Η εντολή switch... 158 4.6 Η εντολή while... 161 Χρήση του βρόχου while 162, Ατέρμονες βρόχοι 164, Επίλυση του προβλήματος του "μισού βρόχου" 165 4.7 Η εντολή for... 167 Ένθετοι βρόχοι for 169, Η σχέση μεταξύ των εντολών for και while 171, Χρήση της εντολής for με δεδομένα κινητής υποδιαστολής 172 Σύνοψη...173 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...175 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...176
Περιεχόμενα 23 5 Συναρτήσεις 181 5.1 Χρήση συναρτήσεων βιβλιοθήκης... 182 5.2 Δήλωση συναρτήσεων... 185 5.3 Συγγραφή δικών σας συναρτήσεων... 186 H εντολή return 187, Συνδυασμός συναρτήσεων με κύρια προγράμματα 188, Συναρτήσεις με εσωτερικές δομές ελέγχου 190, Συναρτήσεις που επιστρέφουν μη αριθμητικές τιμές 192, Κατηγορηματικές συναρτήσεις 193, Μια κατηγορηματική συνάρτηση που ελέγχει δύο αλφαριθμητικά για ισότητα 195 5.4 Ο μηχανισμός της διεργασίας κλήσης συναρτήσεων... 196 Μεταβίβαση παραμέτρων 198, Κλήση συναρτήσεων μέσα από άλλες συναρτήσεις 202 5.5 Διαδικασίες... 210 5.6 Βελτίωση βήμα προς βήμα (βηματική εκλέπτυνση)... 211 Ξεκίνημα από "επάνω" 212, Υλοποίηση της PrintCalendar 213, Υλοποίηση της PrintCalendarMonth 214, Συμπλήρωση των τελευταίων κενών 218 Σύνοψη...223 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...225 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...226 6 Αλγόριθμοι 231 6.1 Έλεγχος για το αν ένας αριθμός είναι πρώτος... 232 Μια απλή εκδοχή της συνάρτησης ΙsPrime 233, Επαλήθευση του ότι μια στρατηγική αναπαριστά έναν αλγόριθμο 233, Απόδειξη της ορθότητας του αλγορίθμου ΙsPrime 234, Βελτίωση της αποδοτικότητας ενός αλγορίθμου 236, Επιλογή μεταξύ εναλλακτικών υλοποιήσεων 240 6.2 Υπολογισμός του μέγιστου κοινού διαιρέτη... 241 "Πρωτόγονοι" αλγόριθμοι 241, Ο αλγόριθμος του Ευκλείδη 242, Υπεράσπιση της ορθότητας του αλγορίθμου του Ευκλείδη (προαιρετικό) 243, Σύγκριση της αποδοτικότητας των αλγορίθμων GCD 245 6.3 Αριθμητικοί αλγόριθμοι... 245 Διαδοχικές προσεγγίσεις 245, Αναφορά σφαλμάτων 248 6.4 Ανάπτυγμα σειράς... 249 Το παράδοξο του Ζήνωνα 249, Χρήση του αναπτύγματος σειράς στη συνάρτηση της τετραγωνικής ρίζας 252, Ανάπτυγμα της σειράς Taylor για την προσέγγιση τετραγωνικής ρίζας (προαιρετικό) 252, Υλοποίηση της προσέγγισης με σειρά Taylor 254, Περιοχή σύγκλισης 256 6.5 Καθορισμός του μεγέθους των αριθμητικών τύπων... 259 Τύποι ακεραίων 260, Μη προσημασμένοι τύποι 261, Τύποι κινητής υποδιαστολής 261 Σύνοψη...262 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...263 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...264
24 Περιεχόμενα ΜΕΡΟΣ ΔΕΥΤΕΡΟ Βιβλιοθήκες και ανάπτυξη σε λειτουργικές μονάδες...269 7 Βιβλιοθήκες και διασυνδέσεις: Μια απλή βιβλιοθήκη γραφικών 271 7.1 Η έννοια μιας διασύνδεσης... 272 Διασυνδέσεις και αρχεία κεφαλίδας 274 7.2 Μια εισαγωγή στη βιβλιοθήκη γραφικών... 275 To μοντέλο στο οποίο βασίζεται η διασύνδεση graphics.h 276, Οι συναρτήσεις της διασύνδεσης graphics.h 278, Ανάθεση αρχικών τιμών του πακέτου 282, Σχεδίαση ευθειών 282, Σχεδίαση κύκλων και τόξων 284, Λήψη πληροφοριών για το παράθυρο γραφικών 286 7.3 Δημιουργία δικών σας εργαλείων... 287 Ορισμός της διαδικασίας DrawBox 287, Ορισμός της συνάρτησης DrawCenteredCircle 290, Εναλλαγή μεταξύ απόλυτων και σχετικών συντεταγμένων 290, Τα πλεονεκτήματα του ορισμού διαδικασιών 291 7.4 Επίλυση ενός μεγαλύτερου προβλήματος... 292 Χρήση βηματικής εκλέπτυνσης 293, Υλοποίηση της διαδικασίας DrawHouse 295, Αναζήτηση κοινών προτύπων 295, Ολοκλήρωση της αποσύνθεσης 297 Σύνοψη...302 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...303 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...304 8 Σχεδίαση διασυνδέσεων: Μια βιβλιοθήκη τυχαίων αριθμών 311 8.1 Σχεδίαση διασύνδεσης... 312 Η σημασία ενός ενοποιητικού θέματος 313, Απλότητα και η αρχή της απόκρυψης πληροφοριών 314, Ικανοποίηση των αναγκών των πελατών σας 315, Τα πλεονεκτήματα των γενικών εργαλείων 316, Η αξία της σταθερότητας 317 8.2 Παραγωγή τυχαίων αριθμών με υπολογιστή... 318 Ντετερμινιστική και μη ντετερμινιστική συμπεριφορά 318, Τυχαίοι και ψευδοτυχαίοι αριθμοί 318, Παραγωγή ψευδοτυχαίων αριθμών στην ANSI C 319, Αλλαγή του διαστήματος τιμών των τυχαίων αριθμών 321, Γενίκευση του προβλήματος 326 8.3 Αποθήκευση εργαλείων σε βιβλιοθήκες... 328 Τα περιεχόμενα μιας διασύνδεσης 329, Συγγραφή της διασύνδεσης random.h 331, Η υλοποίηση random.c 331, Δόμηση ενός προγράμματοςπελάτη 333, Ανάθεση αρχικής τιμής στη γεννήτρια τυχαίων αριθμών 335 8.4 Αποτίμηση της σχεδίασης της διασύνδεσης random.h... 338 Δημιουργία τυχαίων πραγματικών αριθμών 340, Προσομοίωση ενός πιθανοτικού ενδεχομένου 340, Συμπερίληψη αρχείων κεφαλίδας σε μια διασύνδεση 341, Ολοκλήρωση της υλοποίησης του πακέτου παραγωγής τυχαίων αριθμών 342 8.5 Χρήση του πακέτου παραγωγής τυχαίων αριθμών... 342 Σύνοψη...346
Περιεχόμενα 25 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...348 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...349 9 Αλφαριθμητικά και χαρακτήρες 359 9.1 Η αρχή της απαρίθμησης... 360 Αναπαράσταση απαριθμητών τύπων στο εσωτερικό του μηχανήματος 362, Αναπαράσταση απαριθμητών τύπων ως ακεραίων 362, Ορισμός νέων απαριθμητών τύπων 363, Πράξεις με απαριθμητούς τύπους 366, Βαθμωτοί τύποι 367 9.2 Χαρακτήρες... 367 Ο τύπος δεδομένων char 367, Ο κώδικας ASCII 368, Σταθερές χαρακτήρων 370, Σημαντικές ιδιότητες της κωδικοποίησης ASCII 370, Ειδικοί χαρακτήρες 371, Αριθμητική χαρακτήρων 373, Η διασύνδεση ctype.h 374, Εντολές ελέγχου που περιλαμβάνουν χαρακτήρες 376, Είσοδος και έξοδος χαρακτήρων 377 9.3 Τα αλφαριθμητικά ως αφηρημένα δεδομένα... 377 Στρωματοποιημένες αφαιρέσεις 378, Η έννοια ενός αφηρημένου τύπου 380 9.4 Η διασύνδεση strlib.h... 381 Προσδιορισμός του μήκους ενός αλφαριθμητικού 381, Επιλογή χαρακτήρων από ένα αλφαριθμητικό 382, Συνένωση αλφαριθμητικών 383, Μετατροπή χαρακτήρων σε αλφαριθμητικά 384, Εξαγωγή τμημάτων ενός αλφαριθμητικού 385, Σύγκριση ενός αλφαριθμητικού με ένα άλλο 386, Αναζήτηση στο εσωτερικό ενός αλφαριθμητικού 388, Μετατροπή πεζών/κεφαλαίων 391, Αριθμητική μετατροπή 392, Αποδοτικότητα της βιβλιοθήκης strlib.h 393 Σύνοψη...394 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...395 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...397 10 Αρθρωτή ανάπτυξη προγραμμάτων 403 10.1 Pig Latin μια περίπτωση αρθρωτής ανάπτυξης προγράμματος... 406 Εφαρμογή αναλυτικής σχεδίασης (σχεδίασης "από επάνω προς τα κάτω") 407, Χρήση ψευδοκώδικα 408, Υλοποίηση της συνάρτησης TranslateLine 409, Αναζήτηση κενών διαστημάτων και σημείων στίξης 410, Εκλέπτυνση του ορισμού της λέξης 412, Σχεδίαση του σαρωτή γλωσσικών σημείων 413, Ολοκλήρωση της υλοποίησης της συνάρτησης TranslateLine 415, Καθορισμός της διασύνδεσης της λειτουργικής μονάδας σάρωσης 418 10.2 Διατήρηση της εσωτερικής κατάστασης μέσα σε μια λειτουργική μονάδα 418 Καθολικές μεταβλητές 421, Οι κίνδυνοι από τη χρήση καθολικών μεταβλητών 422, Μεταβλητές που είναι ιδιωτικές για μια λειτουργική μονάδα 423, Ανάθεση αρχικών τιμών σε καθολικές μεταβλητές 423, Ιδιωτικές συναρτήσεις 426 10.3 Υλοποίηση της αφαίρεσης σαρωτή... 427 Σύνοψη...433 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...434 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...435
26 Περιεχόμενα ΜΕΡΟΣ ΤΡΙΤΟ Σύνθετοι τύποι δεδομένων...441 11 Πίνακες 443 11.1 Εισαγωγή στους πίνακες... 445 Δήλωση πίνακα 445, Επιλογή στοιχείων πίνακα 447, Παράδειγμα απλού πίνακα 448, Αλλαγή του διαστήματος τιμών των αριθμοδεικτών 448 11.2 Εσωτερική αναπαράσταση δεδομένων... 450 Μπιτ, μπάιτ, και λέξεις 451, Διευθύνσεις μνήμης 452, Ο τελεστής sizeof 454, Κατανομή μνήμης σε μεταβλητές 455, Αναφορές σε στοιχεία έξω από τα όρια ενός πίνακα 457 11.3 Μεταβίβαση πινάκων ως παραμέτρων... 459 Γενίκευση του πλήθους των στοιχείων 460, Ο μηχανισμός μεταβίβασης πίνακα ως παραμέτρου 462, Υλοποίηση των συναρτήσεων PrintIntegerArray και GetIntegerArray 465, Υλοποίηση της συνάρτησης ReverseIntegerArray 467, Υλοποίηση της συνάρτησης SwapIntegerElements 468 11.4 Χρήση πινάκων για διάταξη δεδομένων... 473 11.5 Στατική ανάθεση αρχικών τιμών σε πίνακες... 480 Αυτόματος προσδιορισμός του μεγέθους ενός πίνακα 480, Προσδιορισμός του μεγέθους ενός πίνακα στον οποίο έχουν ανατεθεί αρχικές τιμές 481, Πίνακες στους οποίους έχουν ανατεθεί αρχικές τιμές και βαθμωτοί τύποι 482 11.6 Πολυδιάστατοι πίνακες... 482 Μεταβίβαση πολυδιάστατων πινάκων σε συναρτήσεις 484, Ανάθεση αρχικών τιμών σε πολυδιάστατους πίνακες 485 Σύνοψη...486 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...487 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...490 12 Αναζήτηση και ταξινόμηση 499 12.1 Αναζήτηση... 500 Αναζήτηση σε πίνακα ακεραίων 501, Ένα πιο εξεζητημένο παράδειγμα αναζήτησης 503, Γραμμική αναζήτηση 505, Δυαδική αναζήτηση 507, Σχετική αποδοτικότητα των αλγορίθμων αναζήτησης 509 12.2 Ταξινόμηση... 511 Ταξινόμηση πίνακα ακεραίων 512, Ο αλγόριθμος ταξινόμησης με επιλογή 513, Αξιολόγηση της αποδοτικότητας του αλγορίθμου ταξινόμησης με επιλογή 516, Μέτρηση του χρόνου εκτέλεσης ενός προγράμματος 519, Ανάλυση του αλγορίθμου ταξινόμησης με επιλογή 520 Σύνοψη...521 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...521 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...522
Περιεχόμενα 27 13 Δείκτες 529 13.1 Χρήση διευθύνσεων ως τιμών δεδομένων... 531 13.2 Χειρισμός δεικτών στη C... 533 Δήλωση μεταβλητών δεικτών στη C 534, Βασικές πράξεις δεικτών 535, Ο ειδικός δείκτης NULL 538 13.3 Μεταβίβαση παραμέτρων κατ' αναφορά... 538 Σχεδίαση μιας συνάρτησης SwapInteger 542, Χρήση κλήσης κατ' αναφορά για την επιστροφή πολλών αποτελεσμάτων 543, Ο κίνδυνος της κατάχρησης των κλήσεων κατ' αναφορά 545 13.4 Δείκτες και πίνακες... 546 Αριθμητική δεικτών 547, Νέες δυνατότητες των τελεστών ++ και -- 551, Αύξηση και μείωση δεικτών 554, Σχέση μεταξύ δεικτών και πινάκων 555 13.5 Δυναμική κατανομή μνήμης... 557 Ο τύπος void * 558, Δυναμικοί πίνακες 559, Εντοπισμός λαθών στη συνάρτηση malloc 560, Αποδέσμευση μνήμης 561 Σύνοψη...562 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...564 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...566 14 Αλφαριθμητικά και πάλι 571 14.1 Εννοιολογικές αναπαραστάσεις του τύπου string... 572 Τα αλφαριθμητικά ως πίνακες 572, Τα αλφαριθμητικά ως δείκτες 576, Τα αλφαριθμητικά ως αφηρημένος τύπος 577, Αλφαριθμητικές παράμετροι 577, Αλφαριθμητικές μεταβλητές 578, Διαφορές μεταξύ δεικτών και μεταβλητών πίνακα 580, Επιλογή στρατηγικής για την αναπαράσταση των αλφαριθμητικών 583 14.2 Η βιβλιοθήκη αλφαριθμητικών ANSI... 583 H συνάρτηση strcpy 585, H συνάρτηση strncpy 589, Οι συναρτήσεις strcat και strncat 590, Οι συναρτήσεις strlen, strcmp, και strncmp 591, Οι συναρτήσεις strchr, strrchr, και strstr 592, Μια εφαρμογή των συναρτήσεων αλφαριθμητικών ANSI 593 14.3 Υλοποίηση της βιβλιοθήκης strlib... 596 Υλοποίηση των μεταφερόμενων συναρτήσεων 596, Υλοποίηση των συναρτήσεων κατανομής μνήμης της strlib 597 Σύνοψη...599 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...600 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...600 15 Αρχεία 605 15.1 Αρχεία κειμένου... 606 15.2 Χρήση αρχείων στη C... 608 Δήλωση μιας μεταβλητής FILE * 608, Άνοιγμα αρχείου 609, Εκτέλεση λειτουργιών εισόδου/εξόδου 610, Κλείσιμο αρχείων 610, Καθιερωμένα αρχεία 611
28 Περιεχόμενα 15.3 Είσοδος και έξοδος χαρακτήρων... 611 Ενημέρωση αρχείου 614, Επανάληψη ανάγνωσης χαρακτήρων του αρχείου εισόδου 618 15.4 Είσοδος και έξοδος γραμμών κειμένου... 620 15.5 Μορφοποιημένη είσοδος και έξοδος... 622 Οι τρεις μορφές της συνάρτησης printf 623, Οι συναρτήσεις scanf 623, Ανάγνωση αλφαριθμητικών με τη συνάρτηση scanf 625, Ένα παράδειγμα μορφοποιημένων δεδομένων εισόδου/εξόδου 628, Περιορισμοί στη χρήση της συνάρτησης scanf 631 Σύνοψη...632 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...633 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...634 16 Εγγραφές 643 16.1 Η έννοια της εγγραφής δεδομένων... 644 16.2 Χρήση εγγραφών στη C... 646 Ορισμός νέου τύπου δομής 646, Δήλωση μεταβλητών δομών 647, Επιλογή εγγραφών 648, Ανάθεση αρχικών τιμών σε εγγραφές 648, Απλές εγγραφές 648 16.3 Συνδυασμός εγγραφών και πινάκων... 650 16.4 Δείκτες προς εγγραφές... 653 Ορισμός ενός τύπου "δείκτη προς εγγραφή" 653, Κατανομή χώρου μνήμης για δεδομένα εγγραφών 655, Χειρισμός δεικτών προς εγγραφές 657 16.5 Δημιουργία μιας βάσης δεδομένων με εγγραφές... 657 Δημιουργία της βάσης δεδομένων των υπαλλήλων 658, Χρήση της βάσης δεδομένων 661 16.6 Σχεδίαση μιας εφαρμογής που βασίζεται σε εγγραφές... 662 Η σημασία της χρήσης μιας βάσης δεδομένων 662, Διατύπωση του προβλήματος 663, Σχεδίαση της εσωτερικής αναπαράστασης 664, Σχεδιασμός της εξωτερικής δομής 668, Κωδικοποίηση του προγράμματος 670, Η αξία της καθοδηγούμενης από δεδομένα σχεδίασης 677 Σύνοψη...678 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...681 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...682 17 Με το βλέμμα στο μέλλον 691 17.1 Αναδρομή... 692 Ένα απλό παράδειγμα αναδρομής 693, Η συνάρτηση Factorial 694, Το αναδρομικό άλμα της πίστης 700, Το αναδρομικό υπόδειγμα 701, Παραγωγή μεταθέσεων 702, Αναδρομική σκέψη 705 17.2 Αφηρημένοι τύποι δεδομένων... 706 Η αφαίρεση ουράς 707, Αναπαράσταση τύπων στην αφαίρεση ουράς 708, Η διασύνδεση queue.h 710, Υλοποίηση της αφαίρεσης ουράς 712, Εναλλακτικές υλοποιήσεις της αφαίρεσης ουράς 717
Περιεχόμενα 29 17.3 Ανάλυση αλγορίθμων... 721 Αξιολόγηση της αποδοτικότητας αλγορίθμων 721, Συμβολισμός του μεγάλου όμικρον 722, Επανεξέταση του αλγορίθμου ταξινόμησης με επιλογή 723, Στρατηγικές τύπου "διαίρει και βασίλευε" 725, Συγχώνευση δύο πινάκων 726, Ο αλγόριθμος ταξινόμησης με συγχώνευση 727, Η υπολογιστική πολυπλοκότητα του αλγορίθμου ταξινόμησης με συγχώνευση 729, Σύγκριση των επιδόσεων Ν 2 και N log N 731 Σύνοψη...732 ΕΡΩΤΗΣΕΙΣ ΑΝΑΣΚΟΠΗΣΗΣ...733 ΑΣΚΗΣΕΙΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ...733 ΠΑΡΑΡΤΗΜΑ Α. Σύνοψη της σύνταξης και της δομής της C 741 Α.1 Ανασκόπηση της διεργασίας μεταγλώττισης... 742 Α.2 Ο προεπεξεργαστής της C... 743 Α.3 Η λεξικογραφική δομή των προγραμμάτων C... 745 Α.4 Παραστάσεις... 747 Α.5 Εντολές... 748 Α.6 Συναρτήσεις... 753 Α.7 Δηλώσεις... 754 Α.8 Τύποι δεδομένων... 756 Α.9 Βιβλιοθήκες ANSI... 759 ΠΑΡΑΡΤΗΜΑ Β. Βιβλιοθήκες 767 Ευρετήριο 793
292 ΚΕΦΑΛΑΙΟ 7: Βιβλιοθήκες και διασυνδέσεις: Μια απλή βιβλιοθήκη γραφικών box.c. Μπορείτε να καλέσετε την DrawBox πολλές φορές, ή ακόμα και να την τοποθετήσετε μέσα σε ένα βρόχο for ο οποίος θα σχεδιάζει ένα τετράγωνο σε κάθε κύκλο. 7.4 Επίλυση ενός μεγαλύτερου προβλήματος Για να διευρύνετε την κατανόησή σας σε ό,τι αφορά τις συναρτήσεις της βιβλιοθήκης γραφικών, θα πρέπει να επικεντρώσετε την προσοχή σας σε ένα μεγαλύτερο πρόβλημα. Υποθέστε ότι έχετε αποφασίσει να σχεδιάσετε μια εικόνα του ιδανικού σας σπιτιού με τόσες λεπτομέρειες όσες θα βλέπατε σε μια παιδική ζωγραφιά. Ένα τέτοιο σπίτι φαίνεται στο ακόλουθο διάγραμμα: Παρόλο που η εικόνα έχει πολλά ανεξάρτητα τμήματα, αποτελείται από δύο μόνο θεμελιώδη στοιχεία γραφικών: (1) την ευθεία γραμμή που χρησιμοποιείται στο πλαίσιο του σπιτιού, στην πόρτα, και στα τζάμια των παραθύρων και (2) τον κύκλο που χρησιμοποιείται μόνο για το πόμολο της πόρτας. Αν συνδυάσετε αυτές τις γραμμές και τον κύκλο στα κατάλληλα μεγέθη και τις κατάλληλες θέσεις, μπορείτε να δημιουργήσετε τη συνολική εικόνα. Επιπλέον, σχεδόν όλες οι ευθείες γραμμές είναι διατεταγμένες με τέτοιον τρόπο ώστε να σχηματίζουν ορθογώνια, και έτσι μπορείτε να εκμεταλλευτείτε πλήρως το νέο σύνολο εργαλείων σας. Πριν αρχίσετε να γράφετε το πρόγραμμα, όμως, προσέξτε ότι αυτό το συγκεκριμένο σπίτι έχει πολλές ιδιότητες που καθορίζουν το σχήμα του. Για παράδειγμα, το σπίτι έχει πλάτος 3.0 ίντσες. Η απόσταση από το έδαφος μέχρι τη σοφίτα είναι 2.0 ίντσες, με άλλες 0.7 ίντσες μέχρι την κορυφή της στέγης. Η πόρτα είναι ένα ορθογώνιο με διαστάσεις 0.4 επί 0.7 ίντσες, και κάθε τζάμι παραθύρου είναι επίσης ένα ορθογώνιο με διαστάσεις 0.2 επί 0.25 ίντσες. Αντί να γεμίσετε το πρόγραμμά σας με όλους αυτούς τους αριθμούς, είναι προτιμότερο να δώσετε σε αυτές τις τιμές ονόματα, τα οποία θα μπορείτε στη συνέχεια να χρησιμοποιήσετε στο πρό-
7.4 Επίλυση ενός μεγαλύτερου προβλήματος 293 γραμμα. Το διάγραμμα του σπιτιού που παρουσιάζεται στην παραπάνω εικόνα βασίζεται στις ακόλουθες σταθερές: #define HouseHeight 2.0 #define HouseWidth 3.0 #define AtticHeight 0.7 #define DoorWidth 0.4 #define DoorHeight 0.7 #define DoorknobRadius 0.03 #define DoorknobInset 0.07 #define PaneHeight 0.25 #define PaneWidth 0.2 #define FirstFloorWindows 0.3 #define SecondFloorWindows 1.25 Οι τιμές είναι πραγματικοί αριθμοί που αναπαριστούν ίντσες, και τα ονόματά τους περιγράφουν τη φυσική τους σημασία στα πλαίσια της εικόνας. Στο πρόγραμμα, το γεγονός ότι αυτές οι τιμές αναπαριστώνται με τη χρήση συμβολικών ονομάτων κάνει εύκολη την αλλαγή των διαστάσεων αν, για παράδειγμα, θέλατε το σπίτι να ήταν λίγο πλατύτερο ή να είχε μεγαλύτερα παράθυρα. Χρήση βηματικής εκλέπτυνσης Είστε πλέον έτοιμοι να ξεκινήσετε την υλοποίηση. Όπως εξηγήθηκε στο Κεφάλαιο 5, ο καλύτερος τρόπος για να προσεγγίσετε ένα μεγάλο προγραμματιστικό πρόβλημα είναι να χρησιμοποιήσετε τη στρατηγική της βηματικής εκλέπτυνσης προκειμένου να διαμερίσετε το πλήρες πρόβλημα σε ένα σύνολο απλούστερων προβλημάτων. Για να εφαρμόσετε αυτή τη στρατηγική στο πρόβλημα της σχεδίασης του σπιτιού, πρέπει να ξεκινήσετε από το γενικότερο επίπεδο λεπτομέρειας: θέλετε να σχεδιάσετε ένα σπίτι. Θα πρέπει να δώσετε σε αυτή τη λειτουργία ένα όνομα (για παράδειγμα, DrawHouse) και να την ορίσετε ως διαδικασία. Η υλοποίηση της διαδικασίας DrawHouse θα γίνει έτσι το πρώτο σας υπο-πρόβλημα. Για να ολοκληρώσετε την υλοποίηση, πρέπει στη συνέχεια να χωρίσετε το συνολικό πρόβλημα σε μικρότερα κομμάτια: σχεδίαση του περιγράμματος, της πόρτας, και των παραθύρων. Κάθε μια από αυτές τις λειτουργίες θα γίνει με τη σειρά της ένα υπο-πρόβλημα στο επόμενο επίπεδο λεπτομέρειας. Θα πρέπει να συνεχίσετε αυτή τη στρατηγική μέχρι τη στιγμή που όλα τα υποπροβλήματα θα έχουν αναχθεί σε απλές λειτουργίες οι οποίες θα μπορούν να υλοποιηθούν με τα εργαλεία που έχετε στη διάθεσή σας. Ωστόσο, όπως και στην περίπτωση της διαδικασίας DrawBox, θα πρέπει να προσδιορίσετε αν η διαδικασία DrawHouse απαιτεί κάποια ορίσματα. Οι διαστάσεις του σπιτιού ορίστηκαν ως σταθερές στην προηγούμενη ενότητα. Επειδή θα πρέπει επίσης να καθορίσετε πού θέλετε να τοποθετηθεί το σπίτι, φαίνεται λογικό για τη διαδικασία DrawHouse να δέχεται ως ορίσματα μια τετμημένη x και μια τεταγμένη y, που να καθορίζουν τη θέση στην οποία θέλετε να σχεδιαστεί το σπίτι μέσα στο παράθυρο γραφικών. Για λόγους συνέπειας με την DrawBox, είναι λογικό αυτές οι τιμές να καθορίζουν τις συντεταγμένες της κάτω αριστερής γωνίας του σπιτιού. Συνεπώς, το πρωτότυπο της διαδικασίας DrawHouse θα πρέπει να είναι το εξής:
294 ΚΕΦΑΛΑΙΟ 7: Βιβλιοθήκες και διασυνδέσεις: Μια απλή βιβλιοθήκη γραφικών void DrawHouse(double x, double y); Η κλήση αυτής της διαδικασίας δίνει εντολή στον υπολογιστή να σχεδιάσει ένα σπίτι του οποίου η κάτω αριστερή γωνία είναι το σημείο (x, y). Έχοντας ορίσει το πρωτότυπο, μπορείτε πλέον να επιστρέψετε και να ολοκληρώσετε το κυρίως πρόγραμμα. Το μόνο που χρειάζεται να κάνετε στην πραγματικότητα είναι να προσδιορίσετε το σημείο της οθόνης όπου πρέπει να εμφανιστεί η εικόνα του σπιτιού. Για παράδειγμα, υποθέστε ότι θέλετε το σπίτι να είναι κεντραρισμένο στο παράθυρο γραφικών. Όπως αναφέρθηκε στην ενότητα "Λήψη πληροφοριών για το παράθυρο γραφικών", νωρίτερα σε αυτό το κεφάλαιο, μπορείτε να χρησιμοποιήσετε τις συναρτήσεις GetWindowWidth και Get- WindowHeight για να βρείτε τις συντεταγμένες του κέντρου του παραθύρου. Για παράδειγμα, αν δηλώσετε τις μεταβλητές cx και cy, μπορείτε να τους αναθέσετε τις συντεταγμένες του κέντρου του παραθύρου με τις εντολές cx = GetWindowWidth() / 2; cy = GetWindowHeight() / 2; Έτσι όπως ορίσατε, όμως, την DrawHouse, το ίδιο το διάγραμμα σχεδιάζεται με βάση την κάτω αριστερή του γωνία και όχι το κέντρο του. Πώς μπορείτε να συσχετίσετε αυτές τις δύο θέσεις; Γνωρίζετε ότι το σπίτι έχει πλάτος HouseWidth ίντσες. Συνεπώς, η αριστερή πλευρά του σπιτιού πρέπει να βρίσκεται στο μισό αυτής της απόστασης από το κέντρο. Έτσι, αν τοποθετήσετε την αριστερή πλευρά του σπιτιού στην τετμημένη cx HouseWidth / 2 το κέντρο του σπιτιού θα συμπέσει με το κέντρο της οθόνης. Μπορείτε να επαναλάβετε την ίδια τεχνική και για την τεταγμένη y. Η μόνη διαφορά είναι ότι το συνολικό ύψος του σπιτιού είναι το άθροισμα των υψών της ορθογώνιας δομής και της σκεπής. Κατά συνέπεια, η κάτω αριστερή γωνία του σπιτιού πρέπει να έχει την τεταγμένη cy (HouseHeight + AtticHeight) / 2 Τώρα που έχετε τις συντεταγμένες της κάτω αριστερής γωνίας του σπιτιού, μπορείτε να ολοκληρώσετε την υλοποίηση της συνάρτησης main ως εξής: main() double cx, cy; InitGraphics(); cx = GetWindowWidth() / 2; cy = GetWindowHeight() / 2; DrawHouse(cx HouseWidth / 2, cy (HouseHeight + AtticHeight) / 2); Με αυτόν τον ορισμό ολοκληρώνεται το υψηλότερο επίπεδο της αποσύνθεσης του προβλήματος.
7.4 Επίλυση ενός μεγαλύτερου προβλήματος 295 Υλοποίηση της διαδικασίας DrawHouse Σε αυτό το σημείο, θα πρέπει να στρέψετε την προσοχή σας στην υλοποίηση της DrawHouse. Εξοικειωμένοι πλέον με την τεχνική της βηματικής εκλέπτυνσης, θα πρέπει να έχετε ήδη μια ιδέα των θεμελιωδών λειτουργιών. Ο "σκελετός" της διαδικασίας DrawHouse θα πρέπει να έχει αυτή τη μορφή: void DrawHouse(double x, double y) DrawOutline(...); DrawDoor(...); DrawWindows(...); Αυτό που απομένει να κάνετε είναι απλώς να συμπληρώσετε τα ορίσματα. Επειδή οι διαδικασίες DrawOutline, DrawDoor, και DrawWindows δεν έχουν πρόσβαση στις τιμές των τοπικών μεταβλητών x και y της DrawHouse, θα πρέπει να μεταβιβάσετε τις πληροφορίες των συντεταγμένων σε κάθε μια από αυτές. Η ακριβής επιλογή, όμως, των τιμών που θα πρέπει να τους μεταβιβάσετε απαιτεί κάποια σκέψη. Το περίγραμμα του σπιτιού ξεκινά στην ίδια γωνία με το σπίτι, οπότε οι τιμές x και y είναι προφανώς οι ίδιες. Σε ό,τι αφορά την πόρτα, μπορείτε να υπολογίσετε τις συντεταγμένες της και στη συνέχεια να τις μεταβιβάσετε στην DrawDoor. Επειδή υπάρχουν διάφορα παράθυρα που σχεδιάζονται σε σχέση με το πλαίσιο του σπιτιού, η συνάρτηση DrawWindows θα πρέπει λογικά να δέχεται ως ορίσματα τις συντεταγμένες του σπιτιού, παρόλο που στην υλοποίησή της θα πρέπει να υπολογιστούν πιο συγκεκριμένες συντεταγμένες για καθένα από τα παράθυρα. Αν υλοποιήσετε την DrawHouse όπως προτείνεται, θα καταλήξει σε μια μορφή παρόμοια με την ακόλουθη: void DrawHouse(double x, double y) DrawOutline(x, y); DrawDoor(x + (HouseWidth DoorWidth) / 2, y); DrawWindows(x, y); Αναζήτηση κοινών προτύπων Όταν προσεγγίζετε μεγάλα προβλήματα, η βηματική εκλέπτυνση είναι μία από τις πολλές στρατηγικές που μπορείτε να χρησιμοποιήσετε προς όφελός σας. Μια άλλη εξαιρετικά χρήσιμη στρατηγική είναι να προσπαθήσετε να εντοπίσετε κοινά στοιχεία μεταξύ των διαφορετικών τμημάτων ενός μεγάλου προβλήματος, έτσι ώστε να μπορείτε να εφαρμόσετε μια μοναδική τεχνική επίλυσης σε όλα. Ουσιαστικά, αυτή η προσέγγιση συνίσταται στον καθορισμό των εργαλείων που είναι καλύτερα για τη συγκεκριμένη δουλειά. Για παράδειγμα, αν μπορούσατε να επιλύσετε με ευκολία διάφορα τμήματα ενός προβλήματος χρησιμοποιώντας μια διαδικασία που να εκτελεί μια συγκεκριμένη λειτουργία, κατά πάσα πιθανότητα θα άξιζε τον κόπο να δημιουργήσετε αυτή τη διαδικασία. Αν σκεφτείτε το πρόβλημα της σχεδίασης του σπιτιού από αυτή την προοπτική, υπάρχουν διάφορα εργαλεία που θα χρειαζόσασταν, ορισμένα από τα οποία έχετε γράψει ήδη. Για παράδειγμα, αφού το περίγραμμα του σπιτιού είναι ορθογώνιο, όπως και το πλαίσιο της πόρτας και τα παράθυρα, θα μπορούσε να αποδειχθεί πολύ βολικό το εργαλείο DrawBox. Το πόμολο
296 ΚΕΦΑΛΑΙΟ 7: Βιβλιοθήκες και διασυνδέσεις: Μια απλή βιβλιοθήκη γραφικών είναι ένας κύκλος, γεγονός που κάνει λογική τη χρήση της DrawCenteredCircle. Θα πρέπει, όμως, να σκεφτείτε και άλλα εργαλεία που θα μπορούσαν να σας βοηθήσουν. Η σκεπή του σπιτιού είναι τριγωνική. Παρόλο που υπάρχει μόνο ένα τρίγωνο στην εικόνα, ίσως να άξιζε τον κόπο να γράψετε μια διαδικασία DrawTriangle, ιδιαίτερα γιατί έτσι θα την είχατε διαθέσιμη και για άλλα προγράμματα. Ακόμα σημαντικότερο, όμως, είναι να προσέξετε την κανονική δομή των παραθύρων και να εξετάσετε την πιθανότητα συγγραφής μιας γενικότερης διαδικασίας που να επιτρέπει τη σχεδίαση καθενός από τα διαφορετικά σετ παραθύρων. Στην προσπάθειά σας να σχεδιάσετε ένα εργαλείο κατάλληλο για τα παράθυρα, καλό θα ήταν να γενικεύσετε το πρόβλημα όσο περισσότερο μπορείτε. Όσο γενικότερα είναι τα εργαλεία σας, τόσο ευκολότερο θα είναι να τα εφαρμόσετε σε μια μεγάλη ποικιλία περιστάσεων. Ένα τρόπος για να εξασφαλίσετε ότι τα εργαλεία που δημιουργείτε θα είναι ευρέως εφαρμόσιμα είναι να "απομακρυνθείτε" λίγο από τα ειδικά χαρακτηριστικά του τρέχοντος προβλήματος και να προσπαθήσετε να φανταστείτε τις απαραίτητες λειτουργίες σε ένα υψηλότερο, πιο αφαιρετικό επίπεδο. Στα πλαίσια του σπιτιού, η εικόνα παριστάνει ένα παράθυρο με φατνώματα (τζαμάκια). Αν επικεντρώσετε, όμως, την προσοχή σας στην ίδια την εικόνα, θα διαπιστώσετε ότι αυτό που βλέπετε δεν είναι παρά ένα ορθογώνιο πλέγμα αποτελούμενο από δύο σειρές πλαισίων, κάθε μια από τις οποίες περιέχει τρία πλαίσια. Αν είχατε μια διαδικασία DrawGrid που να σχεδιάζει ένα ορθογώνιο πλέγμα, θα μπορούσατε να χρησιμοποιήσετε αυτή τη διαδικασία για να σχεδιάσετε κάθε σετ παραθύρου. Τι ορίσματα απαιτεί η DrawGrid; Για να επιτύχετε την απαιτούμενη γενικότητα, πρέπει να εξασφαλίσετε ότι η διαδικασία DrawGrid δεν θα αναφέρεται στο συγκεκριμένο πλαίσιο του σπιτιού. Επειδή η χρήση των σταθερών PaneWidth και PaneHeight θα εξειδίκευε τη διαδικασία για τη συγκεκριμένη εικόνα του σπιτιού, είναι προτιμότερο το πλάτος και το ύψος κάθε πλαισίου του πλέγματος να μεταβιβάζονται από τον καλούντα ως ορίσματα. Για τη συγκεκριμένη εφαρμογή, επειδή ο καλών θέλει να σχεδιάσει ένα παράθυρο, μπορεί να μεταβιβάσει τις σταθερές PaneWidth και PaneHeight. Η ίδια η διαδικασία απλώς σχεδιάζει πλαίσια. Εκτός από το πλάτος και το ύψος κάθε πλαισίου του πλέγματος, η DrawGrid χρειάζεται να γνωρίζει και τις συντεταγμένες του πλέγματος συνολικά. Για να είναι συνεπής με τα άλλα εργαλεία, η DrawGrid θα πρέπει και αυτή να θεωρεί ότι οι συγκεκριμένες συντεταγμένες αντιστοιχούν στην κάτω αριστερή γωνία του πλέγματος. Τέλος, η διαδικασία πρέπει να γνωρίζει το πλήθος των στηλών και των γραμμών του πλέγματος. Συνεπώς, το πρωτότυπο της DrawGrid θα πρέπει να έχει την εξής μορφή: void DrawGrid(double x, double y, double width, double height, int columns, int rows);
7.4 Επίλυση ενός μεγαλύτερου προβλήματος 297 Με δεδομένο ότι έχετε ήδη τη συνάρτηση DrawBox, η υλοποίηση της DrawGrid είναι σχετικά προφανής. Η υλοποίηση αποτελείται από ένα ζεύγος ένθετων βρόχων for με τους οποίους καλείται η DrawBox για κάθε στήλη κάθε γραμμής, όπως παρακάτω: /* * Συνάρτηση: DrawGrid * Χρήση: DrawGrid(x, y, width, height, columns, rows); * ---------------------------------------------------- * Η DrawGrid σχεδιάζει ορθογώνια διατεταγμένα σε ένα πλέγμα * δύο διαστάσεων. Όπως πάντα, οι συντεταγμένες (x, y) * προσδιορίζουν την κάτω αριστερή γωνία της εικόνας. */ void DrawGrid(double x, double y, double width, double height, int columns, int rows) int i, j; for(i = 0; i < columns; i++) for(j = 0; j < rows; j++) DrawBox(x + i * width, y + j * height, width, height); Τώρα που έχετε την υλοποίηση της DrawGrid, μπορείτε να κατασκευάσετε καθένα από τα πρότυπα παραθύρων καλώντας απλώς την DrawGrid με τα κατάλληλα ορίσματα. Παρόλο που είναι συνήθως καλύτερο να σχεδιάζετε ένα πρόβλημα "από επάνω προς τα κάτω", συχνά είναι προτιμότερο να το υλοποιείτε "από κάτω προς τα επάνω". Η υλοποίηση πρώτα των εργαλείων χαμηλού επιπέδου διευκολύνει την αποσφαλμάτωση των μεμονωμένων τμημάτων του προγράμματός σας, πράγμα που είναι συνήθως ευκολότερο από το να προσπαθείτε να αποσφαλματώσετε ολόκληρο το πρόγραμμα με μιας. Αυτή η στρατηγική ονομάζεται συνθετική υλοποίηση ή υλοποίηση "από κάτω προς τα επάνω" (bottom-up implementation). Ολοκλήρωση της αποσύνθεσης Μετά τη δημιουργία του εργαλείου DrawGrid, το υπόλοιπο του προγράμματος σχεδίασης του σπιτιού αποτελεί ένα ξεκάθαρο παράδειγμα βηματικής εκλέπτυνσης. Το πλήρες πρόγραμμα, house.c, παρουσιάζεται στην Εικόνα 7-7.
298 ΚΕΦΑΛΑΙΟ 7: Βιβλιοθήκες και διασυνδέσεις: Μια απλή βιβλιοθήκη γραφικών ΕΙΚΟΝΑ 7-7 house.c /* * Αρχείο: house.c * ------------- * Αυτό το πρόγραμμα σχεδιάζει ένα απλό πλαίσιο σπιτιού. */ #include <stdio.h> #include "genlib.h" #include "graphics.h" /* * Σταθερές * --------- * Οι ακόλουθες σταθερές καθορίζουν τα μεγέθη * των διαφόρων στοιχείων στην οθόνη. */ #define HouseHeight 2.0 #define HouseWidth 3.0 #define AtticHeight 0.7 #define DoorWidth 0.4 #define DoorHeight 0.7 #define DoorknobRadius 0.03 #define DoorknobInset 0.07 #define PaneHeight 0.25 #define PaneWidth 0.2 #define FirstFloorWindows 0.3 #define SecondFloorWindows 1.25 /* Πρωτότυπα συναρτήσεων */ void DrawHouse(double x, double y); void DrawOutline(double x, double y); void DrawWindows(double x, double y); void DrawDoor(double x, double y); void DrawBox(double x, double y, double width, double height); void DrawTriangle(double x, double y, double base, double height); void DrawCenteredCircle(double x, double y, double r); void DrawGrid(double x, double y, double width, double height, int columns, int rows); /* Κυρίως πρόγραμμα */ main() double cx, cy;
7.4 Επίλυση ενός μεγαλύτερου προβλήματος 299 InitGraphics(); cx = GetWindowWidth() / 2; cy = GetWindowHeight() / 2; DrawHouse(cx - HouseWidth / 2, cy - (HouseHeight + AtticHeight) / 2); /* * Συνάρτηση: DrawHouse * Χρήση: DrawHouse(x, y); * ----------------------- * Αυτή η συνάρτηση σχεδιάζει ένα διάγραμμα σπιτιού με την * κάτω αριστερή του γωνία στο σημείο (x, y). Σε αυτό το * επίπεδο, η συνάρτηση απλώς διαχωρίζει τις εργασίες. */ void DrawHouse(double x, double y) DrawOutline(x, y); DrawDoor(x + (HouseWidth - DoorWidth) / 2, y); DrawWindows(x, y); /* * Συνάρτηση: DrawOutline * Χρήση: DrawOutline(x, y); * ------------------------- * Αυτή η συνάρτηση σχεδιάζει το περίγραμμα του σπιτιού * χρησιμοποιώντας ως αρχή το σημείο (x, y). Το περίγραμμα * αποτελείται από ένα ορθογώνιο με ένα τρίγωνο από επάνω. */ void DrawOutline(double x, double y) DrawBox(x, y, HouseWidth, HouseHeight); DrawTriangle(x, y + HouseHeight, HouseWidth, AtticHeight); /* * Συνάρτηση: DrawDoor * Χρήση: DrawDoor(x, y); * ---------------------- * Αυτή η συνάρτηση σχεδιάζει μια πόρτα με το πόμολό της. Όπως * συνήθως, το σημείο (x, y) προσδιορίζει την κάτω αριστερή γωνία * της πόρτας. */ void DrawDoor(double x, double y) DrawBox(x, y, DoorWidth, DoorHeight); DrawCenteredCircle(x + DoorWidth - DoorknobInset, y + DoorHeight / 2, DoorknobRadius);
300 ΚΕΦΑΛΑΙΟ 7: Βιβλιοθήκες και διασυνδέσεις: Μια απλή βιβλιοθήκη γραφικών /* * Συνάρτηση: DrawWindows * Χρήση: DrawWindows(x, y); * ------------------------- * Αυτή η συνάρτηση σχεδιάζει όλα τα παράθυρα του σπιτιού, * εκμεταλλευόμενη το γεγονός ότι όλα τα παράθυρα αποτελούν * διδιάστατα πλαίσια φατνωμάτων (τζαμιών) ίσου μεγέθους. * Καλώντας τη συνάρτηση DrawGrid, αυτή η υλοποίηση μπορεί * να δημιουργήσει όλες τις δομές παραθύρων χρησιμοποιώντας * ένα μόνο εργαλείο. */ void DrawWindows(double x, double y) double xleft, xright; xleft = x + HouseWidth * 0.25; xright = x + HouseWidth * 0.75; DrawGrid(xleft - PaneWidth * 1.5, y + FirstFloorWindows, PaneWidth, PaneHeight, 3, 2); DrawGrid(xright - PaneWidth * 1.5, y + FirstFloorWindows, PaneWidth, PaneHeight, 3, 2); DrawGrid(xleft - PaneWidth, y + SecondFloorWindows, PaneWidth, PaneHeight, 2, 2); DrawGrid(xright - PaneWidth, y + SecondFloorWindows, PaneWidth, PaneHeight, 2, 2); /* * Συνάρτηση: DrawBox * Χρήση: DrawBox(x, y, width, height); * ------------------------------------ * Αυτή η συνάρτηση σχεδιάζει ένα ορθογώνιο με καθορισμένο πλάτος * και ύψος, και την κάτω αριστερή του γωνία στο σημείο (x, y). */ void DrawBox(double x, double y, double width, double height) MovePen(x, y); DrawLine(0, height); DrawLine(width, 0); DrawLine(0, -height); DrawLine(-width, 0); /* * Συνάρτηση: DrawTriangle * Χρήση: DrawTriangle(x, y, base, height); * ---------------------------------------- * Αυτή η συνάρτηση σχεδιάζει ένα ισοσκελές τρίγωνο (δηλ. με * δύο ίσες πλευρές) με οριζόντια βάση. Οι συντεταγμένες του * αριστερού άκρου της βάσης είναι (x, y) και το τρίγωνο έχει
7.4 Επίλυση ενός μεγαλύτερου προβλήματος 301 * το καθορισμένο μήκος βάσης και ύψος. Αν το ύψος είναι θετικό, * το τρίγωνο "κοιτά" προς τα επάνω. Αν το ύψος είναι αρνητικό, * το τρίγωνο "κοιτά" προς τα κάτω. */ void DrawTriangle(double x, double y, double base, double height) MovePen(x, y); DrawLine(base, 0); DrawLine(-base / 2, height); DrawLine(-base / 2, -height); /* * Συνάρτηση: DrawCenteredCircle * Χρήση: DrawCenteredCircle(x, y, r); * ----------------------------------- * Αυτή η συνάρτηση σχεδιάζει έναν κύκλο με ακτίνα r και * κέντρο το σημείο (x, y). */ void DrawCenteredCircle(double x, double y, double r) MovePen(x + r, y); DrawArc(r, 0, 360); /* * Συνάρτηση: DrawGrid * Χρήση: DrawGrid(x, y, width, height, columns, rows); * ---------------------------------------------------- * Η DrawGrid σχεδιάζει ορθογώνια διατεταγμένα σε ένα πλέγμα * δύο διαστάσεων. Όπως πάντα, οι συντεταγμένες (x, y) * προσδιορίζουν την κάτω αριστερή γωνία της εικόνας. */ void DrawGrid(double x, double y, double width, double height, int columns, int rows) int i, j; for (i = 0; i < columns; i++) for (j = 0; j < rows; j++) DrawBox(x + i * width, y + j * height, width, height);