Τετάρτη 15 Ιουλίου 2009

Καλώς ήρθατε στο C-Guide!


Το συγκεκριμένο blog θα σας βοηθήσει να γνωρίσετε και να εξοικιωθείτε με την γλώσσα προγραμματισμού C.

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



Περιήγηση στο blog

Η προτεινόμενη σειρά επισκόπησης του blog είναι:
1. Υπολογιστής και γλώσσες προγραμματιμού
2. Δομή ενός προγράμματος
3. Εισαγωγικά και ιστορικά στοιχεία για την γλώσσα C
4. Το περιβάλλον προγραμματιμού Dev-C++
5. Παραδείγματα σε C
6. Φύλλα εργασίας
7. Ασκήσεις αυτοαξιολόγησης

Φυσικά καθένας είναι ελεύθερος να επιλέξει τελικά ο ίδιος την σειρά επόπτευσης των αναρτήσεων.

Άλλοι ενδιαφέροντες σύνδεσμοι

CodeLite
The history of computing project
C Language answers to FAQ
Recommended C style and coding standards
MinGW homepage

Ευχαριστούμε!
Τα μέλη του blog, Σιούτης Μιχαήλ, Ζήκου Φιλιππία, Ψαλλιδάς Φώτης
e-mail: aquickguideonc[[[ at ]]]gmail.com

Τρίτη 14 Ιουλίου 2009

Το περιβάλλον προγραμματισμού Dev-C++


Για την εκμάθηση της γλώσσας C και την εξάσκηση πάνω σε αυτή με υλοποίηση κώδικα, έιναι απαραίτητο το περιβάλλον προγραμματισμού Dev-C++.
Σαφής και αναλυτικές οδηγίες εγκατάστασης και χρήσης του Dev-C++ δίνονται στον παρακάτω σύνδεσμο:
Το περιβάλλον προγραμματισμού Dev-C++



Οδηγίες σε περίπτωση δυσκολίας ορθής εγκατάστασης του περιβάλλοντος, παρέχονται στο παρακάτω video:
How to install Dev-C++

Ασκήσεις αυτοαξιολόγησης

Στον παρακάτω σύνδεσμο δίνονται 10 ασκήσεις αυτοαξιολόγησης: exercises


Για διευκρινήσεις πάνω στις ασκήσεις καθώς και βαθμολόγηση των λύσεων αυτών, μπορούν να σταλούν οι αντίστοιχες απορίες καθώς και λύσεις στο e-mail: aquickguideonc[[[ at ]]]gmail.com

Φύλλα Εργασίας

Τα παρακάτω αποτελούν 2 μελέτες παραδείγματος οι οποίες αντιστοιχούν σε 2 διδακτκές ώρες.

Η 1η μελέτη αφορά μια απλή χρήση των δομών επιλογής και επανάληψης και είναι διαθέσιμη στον παρακάτω σύνδεσμο: max grades

Η 2η μελέτη εμβαθύνει στις παραπάνω δομές, με εφαρμογή του αλγορίθμου αναζήτησης bubble sort και είναι διαθέσιμη στον παρακάτω σύνδεσμο: champion team

Σάββατο 11 Ιουλίου 2009

Εισαγωγικά και ιστορικά στοιχεία για τη γλώσσα C

Η γλώσσα C είναι άρρηκτα συνδεδεμένη με το λειτουργικό σύστημα Unix Δημιουργήθηκε στις αρχές της δεκαετίας του 1970 με σκοπό να είναι μια γλώσσα προγραμματισμού που θα διευκόλυνε ακριβώς τη δουλειά της δημιουργίας ενός νέου λειτουργικού συστήματος το οποίο θα ήταν γραμμένο σε μια portable (εύκολα μεταφερόμενη) γλώσσα υψηλού επιπέδου, εκτός από ένα πολύ μικρό κομμάτι του που αναγκαστικά θα γραφόταν σε γλώσσα assembly (τη γλώσσα που βρίσκεται στο χαμηλότερο σχεδόν επίπεδο, δηλ. πλησιέστερα στη μηχανή, και όπου κάθε παραμικρό κομμάτι της μηχανής ελέγχεται πλήρως).

Αυτό το χαρακτηριστικό του λειτουργικού συστήματος Unix βοήθησε τρομερά στο να λειτουργήσει αυτό σε όλων των ειδών τις μηχανές (υπολογιστές διαφορετικών κατασκευαστών) με σχετικά μεγάλη ευκολία, αφού το machine-dependent κομμάτι του ήταν περιορισμένο στο ελάχιστο δυνατό. Έτσι, όταν κάποιος ήθελε να μεταφέρει (port) το Unix σε μια νέα μηχανή, αρκούσε να γράψει ένα μικρό κομμάτι του λειτουργικού συστήματος. Για το υπόλοιπο αρκούσε να έχει ένα C compiler για τη συγκεκριμένη μηχανή. (Είναι σημαντικό να ξεχωρίσουμε εδώ ότι ένας compiler για μια γλώσσα που τρέχει σε μια μηχανή Α μπορεί κάλλιστα να παράγει κώδικα ο οποίος να είναι εκτελέσιμος σε μια μηχανή Β. Αυτοί λέγονται cross-compilers.)

Από τη δεκαετία του 70 και πέρα η C έχει γίνει μια από τις πιο ευρέως διαδεδομένες γλώσσες, ξεφεύγοντας από τα στενά όρια του system programming για το οποίο είχε επινοηθεί. Ήταν μια γλώσσα θεμελιωδώς απλή που παρείχε μεν τη δυνατότητα στον προγραμματιστή για το λεγόμενο δομημένο προγραμματισμό (structured programming) αλλά δεν του έδενε ταυτόχρονα τα χέρια (όπως κάνει π.χ. μια γλώσσα όπως η Pascal, της οποίας οι compilers επιμένουν, κατά παράδοση, στην εφαρμογή ενός στυλ προγραμματισμού και δεν επιτρέπουν παρεκκλίσεις από αυτό.)

Εξ αρχής η C είχε συνδυαστεί με το λεγόμενο C preprossesor (που συνήθως καλείται cpp στο Unix). Αυτός είναι μια ιδιότυπη μορφή προεπεξεργασίας του προγράμματος, που θα δούμε σε λεπτομέρεια αργότερα, και που είναι κατ' ουσίαν ανεξάρτητη της γλώσσας C, και επιτρέπει μια προεπεξεργασία του προγράμματος, σε επίπεδο κειμένου και μόνο, προτού αυτό πάει για μεταγλώττιση στον compiler. Πρέπει δηλ. ο cpp να θεωρείται ένα είδος φίλτρου: το C πρόγραμμα μπαίνει ως είσοδος στον cpp και ένα άλλο C πρόγραμμα βγαίνει ως έξοδος το οποίο στη συνέχεια οδηγείται στον compiler. Αυτός, μετά από ορισμένα ακόμη στάδια επεξεργασίας, θα παραγάγει τον εκτελέσιμο κώδικα. Η προεπεξεργασία με τον cpp παρέχει μια τεράστια ευελιξία στον προγραμματιστή και δύσκολα μπορεί κανείς να φανταστεί πως η C θα είχε πετύχει τόσο πολύ ως γλώσσα προγραμματισμού αν δεν είχε εξ αρχής συνοδευτεί από τον cpp. Θα δούμε αργότερα αρκετά παραδείγματα χρήσης του cpp, πολλά από αυτά καθόλου τετριμμένα.

Στη διεύθυνση http://cm.bell-labs.com/cm/cs/who/dmr/chist.html μπορείτε να διαβάσετε ένα ενδιαφέρον άρθρο του D. Ritchie, που μαζί με τον B. Kernighan, δημιούργησε τη γλώσσα C στα Bell Laborataries. Αφορά την ιστορία της ανάπτυξης της γλώσσας κι ίσως είναι λίγο τεχνικό σε ορισμένα σημεία του.

Δομή ενός προγράμματος

Πρόγραμμα

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

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

Για παράδειγμα, στη γλώσσα προγραμματισμού BASIC, μια από τις απλούστερες ίσως γλώσσες, το παραπάνω πρόγραμμα μπορεί να γραφεί ως εξής:

10 print "Give a: "
20 input a
30 print "Give b: "
40 input b
50 x = 0
60 if a
Η γλώσσα BASIC, στη μορφή αυτή που φαίνεται στο πιο πάνω πρόγραμμα συναντάται πλέον σπάνια. Υπάρχουν πιο εξελιγμένες μορφές της γλώσσας που χρησιμοποιούνται σήμερα.

Παρ' όλα αυτά, στο παραπάνω πρόγραμμα, όπου υπολογίζεται το πηλίκο της διαίρεσης του ακεραίου a διαιρούμενου από τον ακέραιο b (θετικοί ακέραιοι κι οι δυο), εμφανίζονται τα βασικότερα στοιχεία ενός προγράμματος και θα το χρησιμοποιήσουμε ως το βασικό παράδειγμα για την εισαγωγή τους.

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

Ας κάνουμε όμως πρώτα μια περιγραφή της στρατηγικής που χρησιμοποιείται στον αλγόριθμο που υλοποιείται στο παραπάνω πρόγραμμα BASIC. Πώς δηλ. υπολογίζεται το πηλίκο της διαίρεσης a/b;

Το πηλίκο k της διαίρεσης του a διά του b είναι ο μέγιστος ακέραιος τέτοιος ώστε k . b$ \le$a. Ο τρόπος που έχουμε επιλέξει εδώ για να υπολογίσουμε το k είναι το να αρχίσουμε να προσθέτουμε το b στον εαυτό του μέχρι αυτό το άθροισμα να ξεπεράσει το a. Το πλήθος των φορών που προσθέσαμε πριν ξεπεραστεί το a είναι το πηλίκο k.


Μεταβλητές

Οι μεταβλητές (variables) είναι το πιο βασικό κομμάτι ενός προγράμματος. Τις φανταζόμαστε σαν ένα κουτί, που έχει πάντα ένα, συνήθως περιγραφικό, όνομα, και κάποια περιεχόμενα που μπορεί να αλλάζουν από βήμα σε βήμα του προγράμματος. Ακόμη, σε πολλές γλώσσες προγραμματιμού (η C και η παλιά αυτή BASIC που δείχνουμε εδώ συμπεριλαμβάνονται σε αυτές) κάθε μεταβλητή έχει το δικό της σταθερό τύπο δεδομένων που μπορεί να κρατήσει. Για παράδειγμα μια μεταβλητή μπορεί να έχει τύπο ακέραιο αριθμό, πραγματικό αριθμό (συνήθως λέγονται αριθμοί κινητής υποδιαστολής ή floating point numbers), λογική τιμή (true ή false, δηλ. να είναι αληθής ή ψευδής), λέξη (string), κ.ά. Το προηγούμενο πρόγραμμα σε BASIC έχει τρεις μεταβλητές: τα a, b, x, που και οι τρεις είναι τύπου ακέραιου.

Οι δύο μεταβλητές a και b χρησιμοποιούνται κατ' αρχήν ως μεταβλητές εισόδου: εκεί κρατάει το πρόγραμμα, στην αρχή τουλάχιστον, τους δύο ακεραίους των οποίων το πηλίκο θέλουμε να υπολογίσουμε. Αντιθέτως η μεταβλητή x είναι μεταβλητή που χρησιμοποιείται και για να κρατήσει μερικά ενδιάμεσα αποτελέσματα, αλλά είναι και η μεταβλητή που στο τέλος των βασικών υπολογισμών κρατάει το επιθυμητό αποτέλεσμα.


I/O (input/output)

Οι εντολές στις γραμμές 10, 20, 30 και 100 είναι εντολές που είτε διαβάζουν (εντολή input της BASIC) κάποια στοιχεία που ο χρήστης γράφει στο πληκτρολόγιο είτε τυπώνουν (εντολή print της BASIC) στην οθόνη του υπολογιστή ή του τερματικού κάποιο κείμενο. Είναι προφανές ότι δεν μπορεί να υπάρξει χρήσιμο πρόγραμμα χωρίς κάποια τέτοια αλληλεπίδραση με το χρήστη. Οι εντολές αυτές λέγονται εισόδου/εξόδου (I/O).


Εντολές ανάθεσης τιμής σε μεταβλητές

Αυτές είναι οι εντολές 50, 70 και 80. Η 50 θέτει τη μεταβλητή x ίση με 0, η 70 αυξάνει την τιμή της ίδιας μεταβλητής κατά 1 και η 80 αφαιρεί από τη μεταβλητή a την τιμή της μεταβλητής b. Η χρήση του συμβόλου = που μοιάζει κατ' αρχήν αντιφατική σε σχέση με όσα έχει κανείς συνηθίσει από την αλγεβρική του χρήση, πρέπει στη γλώσσα BASIC (όπως και στη γλώσσα C) να ερμηνεύεται ως εξής: αριστερά του = υπάρχει πάντα μια μεταβλητή και δεξιά του υπάρχει πάντα μια παράσταση. Το πρόγραμμα πρώτα κάνει όσους υπολογισμούς χρειάζεται για να υπολογίσει την τιμή της παράστασης (ενδεχομένως χρησιμοποιώντας και την μεταβλητή που υπάρχει αριστερά του =, και το αποτέλεσμα αυτό εκχωρεί στη μεταβλητή που βρίσκεται αριστερά.


Εντολές εκτελούμενες υπό συνθήκη

Η εντολή στη γραμμή 60 αποτελείται από μια συνθήκη (a) και μια εντολή (goto 100) που εκτελείται μόνο αν ισχύει τη συγκεκριμένη εκείνη χρονική στιγμή εκτέλεσης του προγράμματος η συνθήκη.


Εντολές μεταφοράς ροής προγράμματος

Τέλος η εντολή goto που εμφανίζεται στις γραμμές 60 και 90 είναι μια εντολή που δεν κάνει τίποτε άλλο από το να μεταφέρει τον έλεγχο ροής του προγράμματος σε μια συγκεκριμένη γραμμή. Αμέσως μετά τη γραμμή 90, για παράδειγμα, εκτελείται η γραμμή 60 και ποτέ η 100.

Ας δώσουμε τώρα ένα παράδειγμα της ροής του προγράμματος όταν το πρόγραμμα τρέξει με a ίσο με 13, και b ίσο με 10 (εδώ εννοούμε ότι ο χρήστης πληκτρολογεί τους αριθμούς 13 και 10 στο πληκτρολόγιο και με αυτή τη σειρά). Η ροή λοιπόν του προγράμματος σε αριθμούς γραμμών είναι η εξής:

10, 20 (ο χρήστης πληκτρολογεί 13), 30, 40 (ο χρήστης πληκτρολογεί 10), 50, 60, 70, 80, 90, 60, 100 (εκτυπώνεται The quotient a/b is 1).

Υπολογιστής και γλώσσες προγραμματιμού

Ο υπολογιστής

Ο υπολογιστής είναι μια μηχανή που έχει τη δυνατότητα να εκτελεί απλές εντολές. Τι είναι μια απλή εντολή δε χρειάζεται αυτή τη στιγμή να το ορίσουμε ακριβώς. Για παράδειγμα η πρόσθεση δυο αριθμών είναι μια απλή εντολή. Ο υπολογιστής βρίσκεται συνεχώς στο loop (κύκλο που επαναλαμβάνεται) εκτέλεσης εντολών, και εκτελεί εκατοντάδες εκατομμυρίων εντολών ανά δευτερόλεπτο. Υπολογιστής με ταχύτητα 800 MHz εκτελεί περί τα 800 εκατομ. εντολές το δευτερόλεπτο (δεν παίρνουν όλες οι απλές εντολές τον ίδιο χρόνο εκτέλεσης, αλλά δε βλάπτει ιδιαίτερα στην κατανόηση της λειτουργίας της μηχανής το να θεωρήσουμε προς το παρόν ότι αυτό συμβαίνει.)

Σε κάθε κύκλο του ο υπολογιστής (για την ακρίβεια ο μικροεπεξεργαστής που είναι η καρδιά κάθε υπολογιστικού συστήματος) λαμβάνει μια εντολή και την εκτελεί. Μια τέτοια εντολή δεν είναι κατ' ανάγκη αριθμητικής φύσης, και εκεί ακριβώς έγκειται η ισχύς του υπολογιστή. Υπάρχουν, π.χ., εντολές που λένε στο μικροεπεξεργαστή να πάρει ως επόμενη εντολή που θα εκτελέσει την εντολή Α ή την εντολή Β, ανάλογα με το αν η τιμή που υπολόγισε στο αμέσως προηγούμενο βήμα είναι θετική ή αρνητική. Είναι ακριβώς τέτοιες εντολές που δίνουν τη δυνατότητα στον υπολογιστή να εκτελεί περίπλοκα ``προγράμματα'', όπως το επόμενο.

 Διάβασε τις τιμές των φυσικών αριθμών a και b
x <- 0 test: Αν a >= b τότε {
x <- x+1 a <- a - b Πήγαινε στο test }
Τύπωσε "Το πηλίκο της διαίρεσης ", a, "δια ", b, "είναι ", x

Γλώσσα μηχανής και γλώσσες υψηλού
επιπέδου

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

Σε αυτή ακριβώς τη δυσκολία οφείλεται, κατά κύριο λόγο, η ύπαρξη των γλωσσών προγραμματισμού υψηλού επιπέδου όπως οι Fortran (χρονικά η πρώτη τέτοια γλώσσα), BASIC, C, Pascal, Lisp και δεκάδες άλλες.

Όπως θα δούμε παρακάτω οι γλώσσες αυτές παρέχουν σημαντικές ευκολίες στον προγραμματιστή, οι οποίες δεν υφίστανται στη γλώσσα μηχανής, ή έστω στη γλώσσα assembly που αποτελεί ένα ενδιάμεσο σημείο ανάμεσα στη γλώσσα μηχανής και στις γλώσσες υψηλού επιπέδου (πολύ πλησιέστερα όμως στη γλώσσα μηχανής απ' ότι στις τελευταίες).

Επειδή όμως κάθε υπολογιστής μια γλώσσα μόνο ``καταλαβαίνει'' κάθε πρόγραμμα που έχει γραφεί σε μια γλώσσα προγραμματισμού (C, BASIC, Fortran κλπ, ή ακόμη και σε γλώσσα assembly) πρέπει πρώτα να μεταγλωττιστεί σε γλώσσα μηχανής προτού μπορέσει να εκτελεστεί. Η διαδικασία αυτή της μεταγλώττισης συναντάται σε τρεις, λίγο-πολύ, διαφορετικές μορφές:

  1. Προγράμματα που έχουν γραφεί σε γλώσσα assembly ``περνάνε'' από το λεγόμενο assembler, που δεν είναι τίποτε άλλο από ένα εκτελέσιμο (δηλ. ήδη μεταφρασμένο σε γλώσσα μηχανής) πρόγραμμα που διαβάζει το πρόγραμμα assembly και παράγει ένα λειτουργικά ισοδύναμο εκτελέσιμο πρόγραμμα.
  2. Προγράμματα που έχουν γραφεί σε μια γλώσσα υψηλού επιπέδου περνάνε από το λεγόμενο compiler (γίνονται compiled, όπως λέμε).
  3. Υπάρχει και μια τρίτη μορφή μεταγλώττισης ενός προγράμματος, αυτή στην οποία δεν δημιουργείται κάποιο εκτελέσιμο πρόγραμμα το οποίο στη συνέχεια εκτελείται, αλλά ο κύκλος μεταγλώττιση-εκτέλεση είναι πολύ πιο βραχύς, και συνήθως εκτελείται μια ``εντολή'' της γλώσσας υψηλού επιπέδου αμέσως μόλις μεταγλωττιστεί και προτού μεταγλωττιστεί η ``επόμενη'' εντολή. Προγράμματα που κάνουν αυτού του είδους τη μεταγλώττιση-εκτέλεση λέγονται interpreters και χαρακτηριστικό τους είναι ότι συνήθως δε δημιουργούν κάποια μορφή εκτελέσιμου προγράμματος που μπορεί να εκτελεστεί χωρίς τη βοήθεια του interpreter και μόνο από τη μηχανή.
Το φάσμα βέβαια ανάμεσα στο compilation και στο interpretation είναι γεμάτο από ενδιάμεσες καταστάσεις, με πιο συχνή, τελευταία, τη μετατροπή του προγράμματος από τη γλώσσα υψηλού επιπέδου σε μια γλώσσα χαμηλού επιπέδου (που έχει επικρατήσει να λέγεται bytecode) η οποία όμως είναι ανεξάρτητη μηχανής. Γι' αυτό το πρόγραμμα σε μορφή bytecode μπορεί να εκτελεστεί σε οποιαδήποτε μηχανή, αρκεί βέβαια να υπάρχει σε αυτή τη μηχανή ένα πρόγραμμα που διαβάζει bytecode και το εκτελεί. Τέτοια προγράμματα είναι συνήθως πολύ απλά να γραφούν, γιατί ακριβώς το bytecode είναι μια πολύ απλή γλώσσα που γράφεται και διαβάζεται εύκολα από μηχανή.

Το bytecode interpretation προσφέρει μια ενδιάμεση αποδοτικότητα (προγράμματα που έχουν γίνει fully compiled και έχουν μετατραπεί σε εκτελέσιμο κώδικα σε γλώσσα μηχανής είναι πολύ ταχύτερα από προγράμματα που διαβάζονται και εκτελούνται ταυτόχρονα από interpreters, είναι όμως καταδικασμένα να τρέχουν μόνο στον τύπο μηχανής (η έννοια ``τύπος της μηχανής'' εδώ περιλαμβάνει το είδος του hardware αλλά και το είδος και τις διάφορες παραμέτρους του λειτουργικού συστήματος) για τον οποίο έχουν μεταγλωττιστεί. Αντίθετα, ένα πρόγραμμα που τρέχει μέσα από interpreter τρέχει οπουδήποτε υπάρχει εγκατεστημένος ένας interpreter για τη γλώσσα στην οποία έχει το πρόγραμμα γραφεί. Είναι όμως πολύ πιο αργό. Το bytecode προσφέρει μια μέση οδό ανάμεσα στο portability (μεταφερσιμότητα) και στο efficiency, που είναι μάλιστα ιδιαίτερα σημαντική στην εκτέλεση προγραμμάτων διά μέσου του Internet για τον απλούστατο λόγο ότι θέλει κανείς να κάνει κλικ σε ένα πρόγραμμα που βρίσκεται στο τάδε site και αυτό να τρέχει τοπικά, στη μηχανή του. Εκεί έχει σημασία τα προγράμματα να είναι αποθηκευμένα σε γλώσσα ανεξάρτητη μηχανής, μια και δεν μπορεί κανείς να υποθέσει κάτι για ένα remote site, αλλά είναι επίσης σημαντικό να είναι αποθηκευμένα σε μια μορφή που να γίνεται interpreted αποτελεσματικά. Το bytecode προσφέρει μια καλή μέση οδό.