είκτες. Προσπέλαση Μνήµης

Σχετικά έγγραφα
2. Εντολές Εισόδου - Ο Επαναληπτικός Βρόγχος for - Χαρακτήρες διαφυγής

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

int array[10]; double arr[5]; char pin[20]; Προγραµµατισµός Ι

1. Τύποι Μεταβλητών - ηλώσεις Μορφής ΗΕντολήΕξόδουPrintf

Πίνακες. 1 Πίνακες. 30 Μαρτίου 2014

C Programming EPL032 Maria Stavrinou Ioannou Εισαγωγή στη Γλώσσα C

Οι δείκτες στη γλώσσα C

ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ ΥΠΟΛΟΓΙΣΤΩΝ & ΥΠΟΛΟΓΙΣΤΙΚΗ ΦΥΣΙΚΗ

Εισαγωγή στην C. Μορφή Προγράµµατος σε γλώσσα C

ΑΣΚΗΣΗ 2: ΔΟΜΗ ΠΡΟΓΡΑΜΜΑΤΟΣ C, ΧΕΙΡΙΣΜΟΣ ΜΕΤΑΒΛΗΤΩΝ ΚΑΙ ΣΥΝΑΡΤΗΣΕΙΣ ΕΙΣΟΔΟΥ ΚΑΙ ΕΞΟΔΟΥ

Κεφάλαιο 2.6: Είσοδος / Έξοδος εδοµένων, Μορφοποίηση εδοµένων Εξόδου. ( ιάλεξη 7) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ Η/Υ Ακαδημαϊκό έτος ΤΕΤΡΑΔΙΟ ΕΡΓΑΣΤΗΡΙΟΥ #4

Κεφάλαιο , 3.2: Συναρτήσεις II. ( ιάλεξη 12) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

3ο σετ σημειώσεων - Πίνακες, συμβολοσειρές, συναρτήσεις

Δομημένος Προγραμματισμός

ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ ΥΠΟΛΟΓΙΣΤΩΝ & ΥΠΟΛΟΓΙΣΤΙΚΗ ΦΥΣΙΚΗ

H ΓΛΩΣΣΑ C. Μάθηµα 7: Πίνακες. ηµήτρης Ψούνης

Οι εντολές ελέγχου της ροής ενός προγράμματος.

ΕΡΓΑΣΤΗΡΙΟ 2 ΕΙΣΑΓΩΓΗ ΣΤΗ C. Εργαστήριο 2. Τµήµα Πληροφορικής και Τηλεπικοινωνιών

Προγραμματισμός Η/Υ 1 (Εργαστήριο)

scanf() scanf() stdin scanf() printf() int float double %lf float

Διάλεξη 3η: Τύποι Μεταβλητών, Τελεστές, Είσοδος/Έξοδος

Διδάσκων: Κωνσταντίνος Κώστα Διαφάνειες: Δημήτρης Ζεϊναλιπούρ

Η γλώσσα προγραμματισμού C

C: Από τη Θεωρία στην Εφαρµογή 2 ο Κεφάλαιο

a = 10; a = k; int a,b,c; a = b = c = 10;

4. ΒΡΟΧΟΙ ΕΠΑΝΑΛΗΨΗΣ (Α' μέρος: for)

Προγραμματισμός Η/Υ (ΤΛ2007 )

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

ΤΕΜ-101 Εισαγωγή στους Η/Υ Εξεταστική Ιανουαρίου 2011 Θέματα Β

Κεφάλαιο Πίνακες Ι. ( ιάλεξη 15) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

Δομημένος Προγραμματισμός (ΤΛ1006)

5ο σετ σημειώσεων - Δείκτες

ΑΣΚΗΣΗ 2: ΧΕΙΡΙΣΜΟΣ ΜΕΤΑΒΛΗΤΩΝ ΣΤΗ C

Προγραμματισμός Η/Υ (ΤΛ2007 )

Δείκτες (Pointers) Ένας δείκτης είναι μια μεταβλητή με τιμή μια διεύθυνση μνήμης. 9.8

Κεφάλαιο 8.7. Πίνακες & Συναρτήσεις ( ιάλεξη 17) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

Γλώσσα Προγραμματισμού C

Παρακάτω δίνεται o σκελετός προγράμματος σε γλώσσα C. Σχολιάστε κάθε γραμμή του κώδικα.

ΕΡΓΑΣΤΗΡΙΟ 1 ΕΙΣΑΓΩΓΗ ΣΤΗ C. Τµήµα Πληροφορικής και Τηλεπικοινωνιών

ΑΝΑΠΤΥΞΗ ΕΦΑΡΜΟΓΩΝ ΣΕ ΠΡΟΓΡΑΜΜΑΤΙΣΤΙΚΟ ΠΕΡΙΒΑΛΛΟΝ

ΠΑΝΕΠΙΣΤΗΜΙΟ ΚΥΠΡΟΥ ΕΠΛ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ ΜΕΘΟΔΩΝ ΕΠΙΛΥΣΗΣ ΠΡΟΒΛΗΜΑΤΩΝ

Κεφάλαιο 5ο: Εντολές Επανάληψης

Η δήλωση πού δηµιουργεί αποθήκευση τών δεδοµένων ονοµαζεται ορισµός τής µεταβλητής.

Διάλεξη 11η: Δείκτες, μέρος 1

Προγραμματισμός Υπολογιστών & Υπολογιστική Φυσική

Προγραμματισμός Υπολογιστών & Υπολογιστική Φυσική

Κεφάλαιο 3.1, : Συναρτήσεις I. ( ιάλεξη 11) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

Δομημένος Προγραμματισμός (ΤΛ1006)

Προγραµµατιστικές Τεχνικές

Ηλεκτρονικοί Υπολογιστές

printf Οι κωδικοί (format codes) του printf για διάφορους τύπους δεδοµένων είναι:

Τύποι Δεδομένων Είσοδος/Έξοδος

ΔΟΜΗΜΕΝΟΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ

Κεφάλαιο ΙV: Δείκτες και πίνακες. 4.1 Δείκτες.

ΑΣΚΗΣΗ 6: ΔΕΙΚΤΕΣ. Σκοπός της Άσκησης. 1. Εισαγωγικά στοιχεία για τους Δείκτες

Διαδικασιακός Προγραμματισμός

Προγραμματισμός Η/Υ Ι (Χρήση της C) 6 η Θεωρία ΜΟΝΟΔΙΑΣΤΑΤΟΙ ΠΙΝΑΚΕΣ

Βασικές Αρχές Προγραμματισμού

Δομές Δεδομένων (Εργ.) Ακ. Έτος Διδάσκων: Ευάγγελος Σπύρου. Εργαστήριο 3 Επανάληψη Γ μέρος

Επεξεργασία Αρχείων Κειµένου

Οικονόμου Βαγγέλησ Διάλεξη Νο 2. Δομημένοσ Προγραμματιςμόσ - Διάλεξη 2

Διαδικασιακός Προγραμματισμός

Εργαστηριακή Άσκηση 1

Δομημένος Προγραμματισμός

6. ΠΙΝΑΚΕΣ & ΑΛΦΑΡΙΘΜΗΤΙΚΑ

ΥΠΟΛΟΓΙΣΤΕΣ ΙΙ. Τι είναι ; Συναρτήσεις. Παράδειγμα #1. double convert ( double cm ) { double inch;

Προγραμματισμός Ι. Δυναμική Διαχείριση Μνήμης. Δημήτρης Μιχαήλ. Ακ. Έτος Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Δείκτες (Pointers) Ένας δείκτης είναι μια μεταβλητή με τιμή μια διεύθυνση μνήμης. 9.8

Προγραμματισμός Η/Υ 1 (Εργαστήριο)

Εισαγωγή στον προγραμματισμό. Τμήμα Πληροφορικής & Επικοινωνιών ΤΕΙ Σερρών Εργαστήριο 2

Δείκτες & Πίνακες Δείκτες, Πίνακες

Α. unsigned int Β. double. Γ. int. unsigned char x = 1; x = x + x ; x = x * x ; x = x ^ x ; printf("%u\n", x); Β. unsigned char

Κεφάλαιο Αλφαριθµητικές Σειρές Χαρακτήρων (Strings)

Κεφάλαιο 10 ο Υποπρογράµµατα

είκτες και Πίνακες (2)

Δομημένος Προγραμματισμός (ΤΛ1006)

for for for for( . */

ΕΝΤΟΛΕΣ ΕΠΑΝΑΛΗΨΗΣ. for (παράσταση_1; παράσταση_2; παράσταση_3) εντολή επόμενη εντολή

ΔΟΜΗΜΕΝΟΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ

Τμήμα Πληροφορικής & Επικοινωνιών Δρ. Θεόδωρος Γ. Λάντζος

Γλώσσα Προγραμματισμού C

ΠΑΝΕΠΙΣΤΗΜΙΟ ΘΕΣΣΑΛΙΑΣ ΣΧΟΛΗ ΘΕΤΙΚΩΝ ΕΠΙΣΤΗΜΩΝ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ

Εισαγωγή στους Αλγόριθμους και τον Προγραμματισμό. 3η Διάλεξη Είσοδος Δεδομένων Συνθήκες Βρόχοι Παραδείγματα

Εργαστήριο 2ο. Περίγραμμα Εργαστηριακής Άσκησης

Κεφάλαιο 8.7. Πολυδιάστατοι Πίνακες ( ιάλεξη 18) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

ΕΡΓΑΣΤΗΡΙΑΚΕΣ ΑΣΚΗΣΕΙΣ C ΣΕΙΡΑ 2 η

H ΓΛΩΣΣΑ C. Μάθηµα 8: είκτες. ηµήτρης Ψούνης

Η πρώτη παράμετρος είναι ένα αλφαριθμητικό μορφοποίησης

Προγραμματισμός Η/Υ (ΤΛ2007 )

Π. Σταθοπούλου ή Οµάδα Α (Φοιτητές µε µονό αριθµό Μητρώου ) ιδασκαλία : Παρασκευή 11πµ-13µµ ΗΛ7

Προγραμματισμός Υπολογιστών & Υπολογιστική Φυσική

Χαράλαµπος Σκόκος ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ Η/Υ I ΓΛΩΣΣΑ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ C ΕΞΕΤΑΣΕΙΣ ΧΕΙΜΕΡΙΝΟΥ ΕΞΑΜΗΝΟΥ Ερωτήσεις

Προγραμματισμός Ι. Δείκτες. Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Μεθόδων Επίλυσης Προβλημάτων

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

Βήματα: μνήμη 2. Αλγόριθμος βήματα που περιγράφουν την επεξεργασία των δεδομένων. Δομές Δεδομένων + Αλγόριθμοι = Προγράμματα

Α' Εξάμηνο ΕΙΣΑΓΩΓΗ ΣΤΟ ΔΟΜΗΜΕΝΟ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟ

Εισαγωγή στον Προγραμματισμό (με. τη C)

ΑΣΚΗΣΗ 5: ΠΙΝΑΚΕΣ. Σχήµα 1: H έννοια των πινάκων

Transcript:

6. είκτες. Προσπέλαση Μνήµης είκτες. Προσπέλαση Μνήµης Η παρούσα ενότητα αναφέρεται σε ένα σηµαντικό χαρακτηριστικό της C, και της C++, το οποίο απουσιάζει από αρκετές άλλες γλώσσες "υψηλού επιπέδου": τους δείκτες. Οι δείκτες αποτελούν ένα πανίσχυρο εργαλείο της C, διότι επιτρέπουν να έχουµε άµεση προσπέλαση στη µνήµη του υπολογιστή. Μέχρι τώρα, η προσπέλαση µας στη µνήµη γινόταν έµµεσα, π.χ. µε τηδήλωσηµιας µεταβλητής ή ενός πίνακα. Οταν κάνουµε µια δήλωση µεταβλητών της µορφής int k; double a; τότε η οδηγία την οποία δίνουµε στον υπολογιστή είναι"κράτησε µία θέση µνήµης δεσµευµένη για τη µεταβλητή k τύπου ακεραίου, και µία θέση µνήµης δεσµευµένη για τη µεταβλητή a τύπου κινητής υποδιαστολής διπλής ακριβείας." Ο λόγος για τον οποίο πρέπει να δηλώσουµε τον τύπο της µεταβλητής είναι ότι η µνήµη διαθέτει διαφορετικό "χώρο" για µια µεταβλητή τύπου ακεραίου, απ' όσο για µια µεταβλητή κινητής υποδιαστολής. Ετσι, στα περισσότερα σύγχρονα µηχανήµατα, µία θέση µνήµης που αντιστοιχεί σε ακεραίους έχει µήκος 32 bit, ενώ µία θέση µνήµης που αντιστοιχεί σε πραγµατικούς (=αριθµούς κινητής υποδιαστολής) έχει µήκος 64bit ή 128 bit ή και περισσότερο, ανάλογα µε τη ζητούµενη ακρίβεια. Σε κάθε περίπτωση, µπορούµε να φανταστούµε τη µνήµη ως ένα σύνολο από "θέσεις µνήµης", όχι αναγκαστικά του ίδιου µήκους. Κάθε θέση µνήµης έχει ένα αριθµό δηλωτικότηςθέσης. Σε διαδοχικές θέσεις µνήµης ο αριθµός αυξάνει κατά ένα. Ετσι, αν κάποια δήλωση µεταβλητής έχει συνδέσει τη µεταβλητή π.χ. µε τη θέση µνήµης 70000, τότε η επόµενη θέση µνήµης έχει αριθµό 70001, η επόµενη 70002, κ.ο.κ... Από την ώρα που γίνεται µια δήλωση µεταβλητής, π.χ. της µεταβλητής διπλής ακρίβειας a, η µεταβλητή αυτή "συνδέεται" µε µία συγκεκριµένη θέση µνήµης. Κάθε µεταβολή που επέρχεται στη µεταβλητή a µέσα στο πρόγραµµα (από εντολές εκχώρησης κ.λ.π.) σηµαίνει πρακτικά ότι τα περιεχόµενα της συγκεκριµένης, ίδιας πάντα, θέσης µνήµης που έχει συνδεθεί µε τηνa, θα µεταβληθούν επίσης κατά το ίδιο ποσό. Το ποιά ακριβώς θέση µνήµης θα καταλάβει µία δηλωθείσα µεταβλητή δεν εξαρτάται από το πρόγραµµα αλλά από τη διαχείριση µνήµης του λειτουργικού συστήµατος. Είναι δυνατό ένα µέρος της µνήµης να χρησιµοποιείται σε άλλες διεργασίες του λειτουργικού, οπότε αλλάζει ο αριθµός της πρώτης θέσης µνήµης που είναι διαθέσιµη για την καταχώρηση της τιµής της µεταβλητής. Εποµένως, ο συγκεκριµένος αριθµός θέσης µνήµης δεν είναι πάντα προβλέψιµος από το πρόγραµµα. Μπορούµε πάντωςνα δούµε ποιά είναι η θέση µνήµης µιας µεταβλητής. Ηθέσηµνήµης µιας µεταβλητής a συµβολίζεται 58

&a καιαποτελείένανακέραιο. Εποµένως µπορούµε ναδούµε τηθέσηµνήµης της a µε µία κανονική εντολή printf όπως στο εξής παράδειγµα: #include <stdio.h> int main() double a=5; printf("%lf\n",a); printf("%d",&a); return 0; Αφού ορίσαµε τη µεταβλητή a και της εκχωρήσαµε τιµή 5, ζητούµε µε την εντολή printf("%lf\n",a) να µας τυπώσει την τιµή της a, ενώ µε την εντολή printf("%d",&a) ζητούµε ναµας τυπώσει τη θέση µνήµης της a. Σε ένα τρέξιµοσεορισµένο µηχάνηµα παίρνουµε τοαποτέλεσµα: 5.000000 37814104 Ο πρώτος αριθµός είναι η τιµή τηςa, ενώ ο δεύτερος αριθµός δείχνει τη θέση µνήµης της a. Ο συγκεκριµένος αριθµός 37814104 της θέσης µνήµης δεν είναι εύκολα προβλέψιµος. Αν το ίδιο πρόγραµµα τρέξει σε άλλο υπολογιστή, ή στον ίδιο υπολογιστή τρέχουν και άλλες εφαρµογές, είναι πιθανό ότι δεν θα πάρουµε ωςαποτέλεσµα την ίδια θέση µνήµης για την a.. Είναι επίσης δυνατή η αντίστροφη διαδικασία, δηλ. ηανάγνωσητουπεριεχόµενου µιας θέσης µνήµης. Το περιεχόµενο µιας θέσης µνήµης αποδίδεται µε τοσυµβολισµό *θέση µνήµης δηλαδή µε αστερίσκο µπροστά από τον αριθµό θέσης µνήµης. Ετσι, ο συµβολισµός *(&a) σηµαίνει "το περιεχόµενο της θέσης µνήµης της µεταβλητής a", το οποίο είναι πρακτικά ίσο µε την τιµή της µεταβλητής a. Η ευκολία που παρέχει η C είναι ότι µας δίνει τη δυνατότητα να ορίζουµε µεταβλητές που δείχνουν σε ποιά θέση µνήµης έχει καταχωρηθεί η τιµή µιας άλλης µεταβλητής οποιουδήποτε τύπου. Οι ειδικές αυτές µεταβλητές που δείχνουν θέση µνήµης ονοµάζονται δείκτες (pointers). Η δήλωση µιας µεταβλητής δείκτη γίνεται όπως και η δήλωση µιας συνήθους µεταβλητής, µε την προσθήκη ενός αστερίσκου πριν από το όνοµα τουδείκτη. Π.χ. οι δηλώσεις double *p; int *q; σηµαίνουν: "έστω ο p δείκτης της θέσης µνήµης κάποιας µεταβλητής διπλής ακρίβειας, και ο q δείκτης της θέσης µνήµης κάποιας µεταβλητής ακέραιου τύπου". Προσοχή χρειάζεται στο γεγονός ότι η δήλωση ενός δείκτη δεν προσδιορίζει ποιάς συγκεκριµένης µεταβλητής τη θέση µνήµης θα δείχνει ο δείκτης, αλλά µόνο τί τύπου µεταβλητής τη θέση µνήµης θα δείχνει ο δείκτης. Κυρίως θα πρέπει να αποφευχθεί η παρεξήγηση ότι ο δείκτης *p δείχνει τη θέση µνήµης µιας µεταβλητής 59

µε τοίδιοόνοµα (δηλαδή της p). Εδω το όνοµα p αναφέρεται στο δείκτη και όχι στη µεταβλητή της οποίας τη θέση µνήµης δείχνει. Επίσης επισηµαίνεται ότι, παρά τη διαφορετική τους δήλωση, τόσο ο p όσο και ο q παίρνουν ακέραιες τιµές (που αντιστοιχούν σε αριθµούς θέσεων µνήµης) και όχι τιµές διπλής ακριβείας και ακέραιες αντίστοιχα. Οι δείκτες παίρνουν πάντα ακέραιες τιµές, αλλά οι µεταβλητές, των οποίων τη θέση µνήµης δείχνουν οι δείκτες, µπορούν να είναι οποιουδήποτε τύπου (int, float, double, char, κ.λ.π.). Τα παραπάνω γίνονται περισσότερο κατανοητά µε το παράδειγµα του προγράµµατος 17: Πρόγραµµα 17 Γραµµή #include <stdio.h> 1 int main() 2 double a=5,b; double *p; 3 p=&a; 4 printf("%lf\n",a); 5 printf("%d\n",p); 6 *p=23; 7 printf("%lf\n",a); 8 b=(*p)/2; 9 printf("%lf\n",b); 10 p=&b; 11 printf("%d\n",p); 12 return 0; Γραµµή 3: double a=5,b; double *p; Στη γραµµή 3 ορίζονται δύο µεταβλητές διπλής ακρίβειας (η a µε τιµή 5 και η b), ενώ ορίζεται και οδείκτηςp ο οποίος θα δείχνει τη θέση µνήµης µιας µεταβλητής διπλής ακρίβειας. Γραµµές 4-6 p=&a; printf("%lf\n",a); printf("%d\n",p); Στη γραµµή 4 οδείκτηςp φορτώνεται τιµή ίσηµε τηθέσηµνήµης της µεταβλητής a. Οι εντολές printf στις γραµµές 5 και 6 ζητούν να τυπωθεί στην οθόνη η τρέχουσα τιµή τηςa, και η τρέχουσα τιµή τουδείκτηp. Επισηµαίνεται ότι η εντολή printf στη γραµµή 6 ζητά να τυτπωθεί ο p µε δήλωση µορφής ακεραίου, µια και οι δείκτες φέρουν πάντα ακέραιες τιµές. Η τρέχουσα τιµή της a είναι 5, ενώ ο υπολογιστής έχει καταχωρήσει την τιµή της a στη θέση µνήµης µε αριθµό 37814104. Εποµένως, ως αποτέλεσµα των δύο εντολών printf των γραµµών 5 και 6 θα τυπωθούν στην οθόνη οι αριθµοί: 5.000000 37814104 60

Γραµµές 7-8 *p=23; printf("%lf\n",a); Στη γραµµή 7 βλέπουµε µία πολύ σηµαντική ιδιότητα των δεικτών: µπορούµε να δώσουµε απευθείας τιµή σε µια θέση µνήµης στην οποία δείχνει ένας δείκτης. Η ακριβής απόδοση της εντολής *p=23; είναι: "να φορτωθεί στη θέση µνήµης, την οποία δείχνει ο p, ητιµή 23". Στο συγκεκριµένο σηµείο του προγράµµατος, οδείκτης, όπως είδαµεπροηγουµένως, δείχνει στη θέση µνήµης µε αριθµό 37814104, η οποία είναι η θέση µνήµης στην οποία καταχωρούνται οι τιµές της µεταβλητής a. Στη συγκεκριµένη θέση µνήµης θα φορτωθεί τιµή ίση µε 23. εδοµένου όµως ότι αυτήηθέση µνήµης συνδέεται µετη µεταβλητή a, η a παίρνει αυτόµατα την τιµή 23. ηλαδή, µία µεταβλητή παρακολουθεί πάντα τις αλλαγές που γίνονται στη θέση µνήµης µε την οποία έχει συνδεθεί, έστω και αν οι αλλαγές αυτές δόθηκαν µε εντολές κατευθείαν στη θέση µνήµης και όχι ως εντολές εκχώρησης της µεταβλητής. Εποµένως, ηεντολήprintf στη γραµµή 8 θα έχει ως αποτέλεσµα την εκτύπωση στην οθόνη της τρέχουσας τιµής της a, δηλαδή 23.000000. Γραµµές 9-10 b=(*p)/2; printf("%lf\n",b); Το ακριβές νόηµα της γραµµής 9 είναι: "να εκχωρηθεί στη µεταβλητή b τιµή ίση µε το περιεχόµενο της θέσης µνήµης την οποία δείχνει ο p, διαιρεµένο µε δύο". Οπως είδαµε πρίν, οδείκτηςp δείχνει τη θέση µνήµης 37814104, στην οποία η τρέχουσα τιµή είναι23. Εποµένως η b θα πάρει τιµή ίση µε 23/2 = 11.5. Το αποτέλεσµα τηςεντολήςprintf στη γραµµή 10 θα είναι να εκτυπωθεί στην οθόνη η τιµή 11.500000 Γραµµές 11-12 p=&b; printf("%d\n",p); Στη γραµµή 11, το πρόγραµµα ζητά να φορτωθεί ο δείκτης p τιµή ίση µε τον αριθµό θέσης µνήµης στην οποία αποθηκεύεται η τιµή της µεταβλητής b. Εστω η b αποθηκεύθηκε στη θέση µνήµης µε αριθµό 37814096. Ο αριθµός αυτός θα είναι η τρέχουσα τιµή του δείκτη p. Εποµένως, η εντολή printf στη γραµµή 12 θα έχει ως αποτέλεσµα νατυπωθείστηνοθόνηοαριθµός 37814096 Χρησιµότητα των δεικτών. Είναι αδύνατο να αναλύσουµε στο σύνολό της τη χρησιµότητατωνδεικτώνστονπρογραµµατισµό. Υπάρχει ένα µεγάλο πλήθος περιπτώσεων όπου χρειαζόµαστε τη δυνατότητα άµεσης πρόσβασης στη µνήµη, τόσο διαβάζοντας το περιεχόµενο, όσο και εκχωρώντας τιµή σε µία ή περισσότερες θέσεις µνήµης. Εδώ θα αναφέρουµε µία µόνο σηµαντική εφαρµογή: τη δυνατότητα, µε τη χρήση δεικτών, να είναι ορατές από ένα πρόγραµµα µεταβολές που συντελούνται στα ορίσµατα µιας συνάρτησης στο εσωτερικό της συνάρτησης. 61

Η εφαρµογή αυτή γίνεται κατανοητή µε το εξής παράδειγµα. Εστω θέλουµε να ορίσουµε µία συνάρτηση rads(x) τύπου void, η οποία να δέχεται ως όρισµα ένα πραγµατικό αριθµό που αντιστοιχεί στην τιµή µιας γωνίας σε µοίρες, και να µετατρέπει το όρισµα απευθείας σε ακτίνια. Π.χ., θέλουµε, αν το πρόγραµµα εκχωρείστηνx την τιµή 30 (δηλαδή 30 µοίρες), µετά την κλίση της rads(x) ητιµή της µεταβλητής x πρέπει να έχει µετατραπεί και γίνει ίση µε π/6 = 0.523598... Ησυνάρτησηθα βασίζεται στο γνωστό τύπο µετατροπής από µοίρες σε ακτίνια: φ(rad) = φ(ο) *π / 180 Εκ πρώτης όψεως, φαίνεται ότι αρκεί να δοθεί στο εσωτερικό της συνάρτησης ένας τύπος εκχώρησης παρόµοιος µε τον παραπάνω τύπο µετατροπής, όπως φαίνεται στο παρακάτω πρόγραµµα: #include<stdio.h> void rads(double); int main() double a; printf("dose ti gonia se moires "); scanf("%lf",&a); rads(a); printf("h gonia se aktinia einai %lf\n",a); return 0; void rads(double x) double pi=3.14159; x=x * pi / 180.; Η εντολή scanf στην τρίτη γραµµή διαβάζει από το πληκτρολόγιο µία τιµή για τη µεταβλητή a(σε µοίρες). Στη συνέχεια καλείται η rads µε όρισµα την τιµή της a. Στον ορισµό της συνάρτησης, δίνεται η εντολή εκχώρησης x = x * pi / 180. που σηµαίνει ότι η τιµή του ορίσµατος θα πρέπει να µετατραπεί κατά το γνωστό τύπο. Μετά την εκτέλεση της εντολής η x έχει µια νέα τιµή. Ετσι ελπίζουµε ότι αφού η rads καλείται στο κυρίως πρόγραµµα µε όρισµα a, η τιµή του ορίσµατος θα έχει µετατραπεί µετά την κλίση της rads, και εποµένως η εντολή printf στο τέλος του προγράµµατος θα τυπώσει στην οθόνη την αναµενόµενη τιµή τηςa, δηλαδή τη γωνία σε ακτίνια. Στην πραγµατικότητα, σε αρκετές γλώσσες προγραµµατισµού αυτό ακριβώς θα συνέβαινε, δηλαδή αλλαγές που συντελούνται στο εσωτερικό συναρτήσεων σε κάποια από τα ορίσµατά της, γίνονται αντιληπτές και περνούν στα ορίσµατα που έχουν δοθεί στο κυρίως πρόγραµµα για την κλήση της συνάρτησης. 62

Στη C όµως αυτό δεν ισχύει. Οι αλλαγές των µεταβλητών στο εσωτερικό µιαςσυνάρτησηςδεν γίνονται αντιληπτές από οποιοδήποτε κοµµάτι κώδικα έξω από τη συνάρτηση. Προσοχή χρειάζεται στο ότι η παραπάνω διαπίστωση δεν επηρεάζεται καθόλου από πιθανόν ίδια ονόµατα των µεταβλητών στη συνάρτηση και στην καλούσα συνάρτηση. Π.χ. αν είχαµε γράψειτο πρόγραµµα στηµορφή #include<stdio.h> void rads(double); int main() double a; printf("dose ti gonia se moires "); scanf("%lf",&a); rads(a); printf("h gonia se aktinia einai %lf\n",a); return 0; void rads(double a) double pi=3.14159; a=a * pi / 180.; ονοµάζοντας δηλαδή a τη µεταβλητή του ορίσµατος στον ορισµό της συνάρτησης, οι µεταβολές της µεταβλητής a εκεί δεν θα είχαν καµµία επίδραση στις τιµές της a στο κυρίως πρόγραµµα, έστω και αν η rads στο κυρίως πρόγραµµα καλείταιµε όρισµα a. Αυτό τονίζει το γεγονός ότι οι µεταβλητές µιας συνάρτησης έχουν καθαρά τοπική εµβέλεια, έστω και αν µοιράζονται το ίδιο όνοµα µε µεταβλητές του κυρίως προγράµµατος ή άλλων συναρτήσεων. Με τους δείκτες ξεπερνούµε τη δυσκολία αυτή, και επιφέρουµε µεταβολές απευθείας στο περιεχόµενο µιας θέσης µνήµης. Οι αλλαγές αυτές, έστω και αν προκληθούν στο εσωτερικό µιας συνάρτησης, είναι µόνιµες, και εποµένως γίνονται αντιληπτές από το υπόλοιπο πρόγραµµα. Η επίλυση του προβλήµατος µε τηχρήσηδεικτώνµπορεί να γίνει όπως στο επόµενο πρόγραµµα 18: Πρόγραµµα 18 Γραµµή #include<stdio.h> 1 void rads(double *); 2 int main() 3 double a; 4 printf("dose ti gonia se moires "); 5 scanf("%lf",&a); 6 rads(&a); 7 printf("h gonia se aktinia einai %lf\n",a); 8 return 0; 9 63

void rads(double *x) 10 double pi=3.14159; 11 *x=(*x) * pi / 180.; 12 Στη γραµµή 2 δηλώνεται ότι το πρόγραµµα θαχρησιµοποιήσει µία συνάρτηση τύπου void µε όνοµα rads και όρισµα έναδείκτη µεταβλητής διπλής ακρίβειας. Μετάαπότοδιάβασµα τηςτιµής της a απότοπληκτρολόγιο, λόγω της γραµµής 6, ακολουθεί η κλήση της rads. Αλλά εδώ είναι η κεφαλαιώδης διαφορά από το προηγούµενο πρόγραµµα: η rads δεν καλείται µεόρισµατηνa, αλλά τη θέση µνήµης της a. Οποιεσδήποτε µεταβολές επέλθουν εκεί θα είναι µόνιµες και άµεσα αντιληπτές από την a. Στη γραµµή 10, αντίστοιχα, η rads ορίζεται να δέχεται ως όρισµα όχι µια µεταβλητή διπλής ακρίβειας, αλλά ένα δείκτη, τον x, ο οποίος θα δείχνει τη θέση µνήµης µιας µεταβλητής διπλής ακρίβειας. Εστω τώρα ότι η µεταβλητή a είχε διασωθεί π.χ. στη θέση µνήµης µε αριθµό 1073245, πρίν από την κλήση της rads στη γραµµή 7. Ηεντολή rads(&a); στη γραµµή 7 καλεί τη rads µε όρισµα τηθέσηµνήµης της a, δηλαδή µε όρισµα 1073245. Λόγω της γραµµής 10, ητιµή 1073245 θα είναι η τιµή πουθαπάρειοδείκτηςx στο εσωτερικό της rads. Στη συνέχεια, ηγραµµή 12 *x=(*x) * pi / 180. έχει το νόηµα: "τα περιεχόµενα της θέσης µνήµης, την οποία δείχνει ο δείκτης x, να πολλαπλασιασθούν επί π και να διαιρεθούν µε 180". Μετά την πράξη αυτή, τα περιεχόµενα της θέσης µνήµης 1073245 (που είχε φορτωθεί στον x) θα µετατραπούν κατά το γνωστό τύπο µετατροπής. Αν π.χ. η a είχε αρχικά τιµή 30, ητιµή αυτήθαήταντοαρχικόπεριεχόµενο της θέσης µνήµης 1073245. Μετά την εκτέλεση της γραµµής 12, το νέο περιεχόµενο της θέσης µνήµης 1073245 θα είναι 30*π/180 = 0.523598. Αυτή η αλλαγή είναι µόνιµη και διατηρείται µόλις τελειώσει η εκτέλεση της συνάρτησης. Εποµένως, επιστρέφοντας στο κυρίως πρόγραµµα (γραµµή 8), η µεταβλητή a, που είναι διασυνδεδεµένη µε τη θέση µνήµης 1073245, παίρνει αυτόµατα τη µέα τιµή 0.523598. Αυτή θα είναι και η τιµή που θα εκτυπωθεί στην οθόνη λόγω της εντολής printf στη γραµµή 8. Υπάρχουν δύο ακόµη τρόποι µε τους οποίους µπορούν να γίνουν αντιληπτές έξω από µία συνάρτηση µεταβολές των µεταβλητών στο εσωτερικό της συνάρτησης. Οι τρόποι αυτοί είναι: α) δηλώσεις µεταβλητών µε αναφορά β) δηλώσεις εξωτερικών µεταβλητών Στον πρώτο τρόπο θα αναφερθούµε παρακάτω. Πάντως δεν εφαρµόζεται άν θέλουµε το όρισµα µιαςσυνάρτησηςπουεπηρεάζεταιναείναιπίνακας, ενώ οι δείκτες εφαρµόζονται και σ' αυτή την περίπτωση πολύ αποτελεσµατικά. Ο δεύτερος τρόπος, δήλωση εξωτερικών µεταβλητών, πρέπει να αποφεύγεται ως προγραµµατιστική πρακτική, διότι µπορεί να οδηγήσει σε ανεξέλεγκτες αλλαγές των µεταβλητών που είναι δύσκολο να διορθωθούν εκ των υστέρων. είκτες και Πίνακες Με τον ίδιο τρόπο που ένας δείκτης δείχνει τη θέση µνήµης µιας µεταβλητής, µπορείναδείξεικαι τις θέσεις µνήµης των διαδοχικών στοιχείων ενός πίνακα. Π.χ. έστω ότι έχουµε δηλώσει τον πίνακα a πέντε στοιχείων και τον δείκτη p µε τις εντολές δήλωσης 64

double a[5]; double *p; Τότε µπορούµε θασυνδέσουµε τοδείκτηp µε τηθέσηµνήµης ενός από τα στοιχεία του πίνακα a. Παραδείγµατος χάριν, ηεντολή p=&a[2]; έχει την έννοια "ο δείκτης p να πάρει τιµή ίση µε τον αριθµό της θέσης µνήµης στην οποία αποθηκεύεται η τιµή του στοιχείου a[2] του πίνακα a. Ωστόσο υπάρχει µία σπουδαία ιδιότητα των πινάκων που αναδεικνύει τη στενή τους διασύνδεση µε τους δείκτες: το όνοµα κάθε πίνακα είναι σταθερά-δείκτης που δείχνει τη θέση του µηδενικού του στοιχείου. Με τον όρο "όνοµα" του πίνακα εννοούµε το δηλωτικό του πίνακα χωρίς αγκύλες. Ετσι το όνοµα του πίνακα a[5] είναι a (χωρίς τις αγκύλες), το όνοµα του πίνακα tabl[8] είναι tabl, κ.ο.κ.. Μέσα σε ένα πρόγραµµα, όπου χρησιµοποιείται το όνοµα tabl χωρίς αγκύλες, αυτό θα αποδίδει τον αριθµό θέσης µνήµης του µηδενικού στοιχείου tabl[0]. Οµοίως, η έκφραση tabl+1 αποδίδει τον αριθµό θέσης µνήµης του στοιχείου tabl[1], η έκφραση tabl+2 τον αριθµό θέσης µνήµης του στοιχείου tabl[2] κ.ο.κ.. Τα παραπάνω γίνονται φανερά αν τρέξουµε τοεξήςπρόγραµµα #include <stdio.h> int main() float a[5]; int i; printf("%d\n",a); printf("%d\n",a+1); printf("%d\n",a+2); printf("%d\n",a+3); printf("%d\n",a+4); return 0; στο οποίο ορίζεται ένας πίνακας a[5] πέντε στοιχείων. Οι πέντε εντολές printf που ακολουθούν δίνουν την οδηγία να τυπωθεί στην οθόνη η έκφραση a, που σηµαίνει "θέση µνήµης του στοιχείου a[0]", ακολουθούµενη από την έκφραση a+1, που σηµαίνει "θέση µνήµης του στοιχείου a[1]", κ.ο.κ.. Αν το πρόγραµµα, µε τηδήλωσητουπίνακα,.δέσµευσε πέντε θέσεις µνήµης γιαταστοιχεία του πίνακα, ξεκινώντας από τη θέση µνήµης 37814080, τότε τα πέντε διαδοχικά printf θα έχουν ως αποτέλεσµα την εκτύπωση στην οθόνη των θέσεων µνήµης των πέντε διαδοχικών στοιχείων του πίνακα 37814080 37814084 37814088 37814092 37814096 65

Παρατηρούµε ότι κάθε επόµενος αριθµός θέσεως µνήµης δεν διαφέρει από τον προηγούµενο κατά ένα, αλλά κατά τέσσερα. Αυτό συµβαίνει διότι χρειάζονται 4byteµνήµης για να αποθηκευτεί ένα στοιχείο του πίνακα, που είναι τύπου float. Το ότι οι αριθµοί που τυπώνονται στην οθόνη αντιστοιχούν πράγµατι στις θέσεις µνήµης των στοιχείων του πίνακα επαληθεύεται εύκολα αν ορίσουµε και µία µεταβλητή δείκτη η οποία να δείχνει τη θέση µνήµης κάθε διαδοχικού στοιχείου του πίνακα, όπως στο επόµενο παράδειγµα #include <stdio.h> int main() float a[5],*p; int i; for(i=0;i<=4; i++) p=&a[i]; printf("%d %d \n",a+i,p); return 0; Λόγω της εντολής p=&a[i]; στο εσωτερικό του βρόγχου, οδείκτηςp δίνει τη θέση µνήµης του στοιχείου a[i] για τα διαδοχικά i=0,1,2,3,4. Τη θέση µνήµης του στοιχείου a[i] αποδίδει οµοίως η έκφραση a+i. Στην εντολή printf("%d %d \n",a+i,p) ζητούµε να τυπωθεί στην οθόνη τόσο η τιµή a+i όσο και η τιµή τουp. Οι δύο εκφράσεις δίνουν το ίδιο αποτέλεσµα καιπαίρνουµε στην οθόνη τους αριθµούς των διαδοχικών θέσεων µνήµης τυπωµένους δύο φορές. 37814080 37814080 37814084 37814084 37814088 37814088 37814092 37814092 37814096 37814096 Υπάρχουν πολλά οφέλη από τη διασύνδεση αυτή δεικτών και πινάκων. Ενα παράδειγµα είναι η εισαγωγή των πινάκων ως ορίσµατα σε συναρτήσεις, κατά τέτοιο τρόπο ώστε µεταβολές του πίνακα στο εσωτερικό της συνάρτησης να γίνονται αντιληπτές ως µόνιµες µεταβολές του πίνακα που δόθηκε ως όρισµα στην κλήση της συνάρτησης από το κυρίως πρόγραµµα. Ως παράδειγµα, δίνουµε τον αλγόριθµο της ταξινόµησης των στοιχείων ενός πίνακα µε τη µέθοδο της "φυσαλίδας". Γιαναγίνεικατανοητήηµέθοδος, εστω τα στοιχεία ενός πίνακα a[8] µε τυχαίες τιµές: a[7]=11 a[6]=8 a[5]=14 a[4]=2 a[3]=19 a[2]=24 a[1]=11 a[0]=7 Θέλουµε να ταξινοµήσουµε τα στοιχεία του πίνακα έτσι ώστε οι τιµές των στοιχείων a[i], µετά την ταξινόµηση, να διατάσσονται κατά φθίνουσα σειρά a[0]>a[1]>a[2]>...>a[7]. 66

Ηταξινόµηση γίνεται ως εξής: ξεκινάµε την πρώτη φορά από κάτω προς τα πάνω, µέχρι να διατρέξουµε όλα τα στοιχεία πλην του τελευταίου. Αν κάποιο στοιχείο a[j] έχει µικρότερη τιµή από το αµέσως επόµενό του a[j+i], εναλλάσσονται οι τιµές των a[j] και a[j+1]. Ετσι, ξεκινώντας από το στοιχείο a[0] έχουµε τις εξής αλλαγές 1ο βήµα: σύγκριση a[0] και a[1] το a[0]<a[1] εποµένως τίθεται a[0]=11 και a[1]=7. Μετά από αυτή την αλλαγή ο πίνακας έχει τη µορφή a[7]=11 a[6]=8 a[5]=14 a[4]=2 a[3]=19 a[2]=24 a[1]=7 a[0]=11 2ο βήµα: σύγκριση a[1] και a[2] το a[1]<a[2] εποµένως τίθεται a[1]=24 και a[2]=7. Μετά από αυτή την αλλαγή ο πίνακας έχει τη µορφή a[7]=11 a[6]=8 a[5]=14 a[4]=2 a[3]=19 a[2]=7 a[1]=24 a[0]=11 3ο βήµα: σύγκριση a[2] και a[3] το a[2]<a[3] εποµένως τίθεται a[2]=19 και a[3]=7. Μετά από αυτή την αλλαγή ο πίνακας έχει τη µορφή a[7]=11 a[6]=8 a[5]=14 a[4]=2 a[3]=7 a[2]=19 a[1]=24 a[0]=11 4ο βήµα: σύγκριση a[3] και a[4] το a[3]>a[4] εποµένως o πίνακας µένει όπως έχει. a[7]=11 a[6]=8 a[5]=14 a[4]=2 a[3]=7 67

a[2]=19 a[1]=24 a[0]=11 5ο βήµα: σύγκριση a[4] και a[5] το a[4]<a[5] εποµένως τίθεται a[4]=14 και a[5]=2. Μετά από αυτή την αλλαγή ο πίνακας έχει τη µορφή a[7]=11 a[6]=8 a[5]=2 a[4]=14 a[3]=7 a[2]=19 a[1]=24 a[0]=11 6ο βήµα: σύγκριση a[5] και a[6] το a[5]<a[6] εποµένως τίθεται a[5]=8 και a[6]=2. Μετά από αυτή την αλλαγή ο πίνακας έχει τη µορφή a[7]=11 a[6]=2 a[5]=8 a[4]=14 a[3]=7 a[2]=19 a[1]=24 a[0]=11 7ο βήµα: σύγκριση a[6] και a[7] το a[6]<a[7] εποµένως τίθεται a[6]=11 και. Μετά από αυτή την αλλαγή ο πίνακας έχει τη µορφή a[6]=11 a[5]=8 a[4]=14 a[3]=7 a[2]=19 a[1]=24 a[0]=11 68

Εδώ τελειώνει ο πρώτος κύκλος ελέγχων. ιαπιστώνουµε ότι ο µικρότερος όλων των αριθµών, ο 2, ανέβηκε σαν "φυσαλίδα" στην κορυφή του πίνακα, ενώ ο αµέσως επόµενος (7), ανέβηκε και αυτός αρκετές θέσεις, αλλά θα πρέπει να ανέβει ακόµη για να έλθει στη σωστή θέση, κάτω από το 2. Επαναλαµβάνουµε την όλη διαδικασία από το στοιχείο a[0] µέχρι το στοιχείο a[5] (που συγκρίνεται µε τοa[6]). Εχουµε έτσι τις διαδοχικές αλλαγές a[6]=11 a[5]=8 a[4]=14 a[3]=7 a[2]=19 a[1]=11 a[0]=24 a[6]=11 a[5]=8 a[4]=14 a[3]=7 a[2]=11 a[1]=19 a[0]=24 a[6]=11 a[5]=8 a[4]=7 a[3]=14 a[2]=11 a[1]=19 a[0]=24 a[6]=11 a[5]=7 a[4]=8 a[3]=14 a[2]=11 a[1]=19 a[0]=24 a[6]=7 a[5]=11 a[4]=8 a[3]=14 a[2]=11 a[1]=19 a[0]=24 Παρατηρούµε ότι η αµέσως µεγαλύτερη τιµή από το 2, δηλαδή η τιµή 7, "τακτοποιήθηκε" τώρα στη σωστή θέση (προτελευταίο στοιχείο του πίνακα), και εποµένως στην επόµενη επανάληψη αρκεί ο έλεγχος να φτάσει στο προ-προτελευταίο στοιχείο του πίνακα. Επαναλαµβάνουµε την όλη διαδικασία από το στοιχείο a[0] µέχρι το στοιχείο a[4] (που συγκρίνεται µε το a[5]). Εχουµε έτσι τις διαδοχικές αλλαγές a[6]=7 a[5]=11 a[4]=8 a[3]=11 a[2]=14 a[1]=19 a[0]=24 a[6]=7 a[5]=8 a[4]=11 a[3]=11 a[2]=14 a[1]=19 a[0]=24 Ηδη στο σηµείο αυτό έχει επιτευχθεί η επιθυµητή ταξινόµηση και οι υπόλοιποι έλεγχοι του αλγόριθµου δεν θα δώσουν καµµία αλλαγή του πίνακα. Ο παραπάνω αλγόριθµος υλοποιείται προγραµµατιστικά όπως φαίνεται στο ακόλουθο πρόγραµµα 19: Πρόγραµµα 19 Γραµµή #include<stdio.h> 1 int main() 2 double a[1000],t; int i,j,n; 3 printf("number of data?\n"); 4 scanf("%d",&n); 5 69

printf("give numbers\n"); 6 for(i=0; i<=n-1; i++) 7 scanf("%lf",&a[i]); 8 for(i=n-2; i>=0; i--) 9 for(j=0; j<=i; j++) 10 if(a[j]<a[j+1]) 11 t=a[j]; a[j]=a[j+1]; a[j+1]=t; 12 printf("sorted numbers\n"); 13 for(i=0;i<n;i++) printf("%lf\n",a[i]); 14 return 0; 15 Ο χρήστης εισάγει το πλήθος επιθυµητό πλήθος στοιχείων στη γραµµή 5, και τα ίδια τα στοιχεία στο βρόγχο των γραµµών 7 και 8. Η "καρδιά" του αλγόριθµουείναιοιγραµµές 9 ως 12. Η µεταβλητή i του εξωτερικού βρόγχου ξεκινάει από το προτελευταίο στοιχείο (το οποίο έχει δείκτη n-2 διότι τα στοιχεία είναι από a[0] ως a[n-1]). Σε κάθε επανάληψη η µεταβλητή j θα "διατρέξει" τις τιµές από 0 ως i, και εποµένως ο έλεγχος της ανίσωσης a[j]<a[j+1], η οποία επιφέρει εναλλαγή των τιµών των a[j] και a[j+1], θα γίνεται για ολοένα και λιγότερα στοιχεία, καθότι, για κάθε τιµή του i"τακτοποιείται" στο άνω µέρος του πίνακα ένα ακόµη στοιχείο. Αν σε κάποια σύγκριση βρεθεί ότι a[j]<a[j+1], πρέπει να γίνει εναλλαγή των τιµών των a[j] και a[j+1]. Αυτό επιτυγχάνεται στις τρεις εντολές της γραµµής 12 t=a[j]; a[j]=a[j+1]; a[j+1]=t;, µε τη βοήθεια της βοηθητικής µεταβλητής t, η οποία διασώζει προσωρινά την παλιά τιµή της µίας εκ των δύο εναλλασσόµενων µεταβλητών. Ηεκτύπωσητουταξινοµηµένου πίνακα στην οθόνη γίνεται µέσω του βρόγχου της γραµµής 14. Σκοπός µας τώρα είναι να γράψουµε τοπρόγραµµα "δοµηµένα", ώστε τόσο η εισαγωγή των δεδοµένων, όσο και η ταξινόµηση και η εκτύπωση να γίνονται µέσα από συναρτήσεις τις οποίες θα καλεί το κυρίως πρόγραµµα. Προτιµούµε τη µέθοδο αυτή, διότι οι συναρτήσεις που θα δηµιουργηθούν θα είναι χρήσιµες και σε πολλά άλλα προγράµµατα. Στο επόµενο πρόγραµµα, το κυρίως πρόγραµµα καλεί τρείς συναρτήσεις, τις void input_numbers(double*,int); void bubble_sort(double*,int); void output_numbers(double*,int); για την εισαγωγή των στοιχείων πίνακα για την ταξινόµηση των στοιχείων πίνακα για την εκτύπωση των στοιχείων πίνακα Η bubble_sort χρησιµοποιεί µία ακόµη συνάρτηση, την toggle(double*, double*), ηοποία πραγµατοποιεί την εναλλαγή των τιµών δύο µεταβλητών #include<stdio.h> void input_numbers(double*,int); void output_numbers(double*,int); void bubble_sort(double*,int); void toggle(double*, double*); int main() double data[1000];int n; printf("number of data?\n"); scanf("%d",&n); input_numbers(data,n); bubble_sort(data,n); printf("sorted numbers\n"); output_numbers(data,n); return 0; 70

void input_numbers(double *x,int n) int i; for(i=0; i<=n-1; i++) scanf("%lf",x+i); void output_numbers(double *x,int n) int i; for(i=0; i<=n-1; i++) printf("%lf\n",*(x+i)); void bubble_sort(double *x,int n) int i,j; for(i=n-2; i>=0; i--) for(j=0; j<=i; j++) if( *(x+j)<*(x+j+1) ) toggle(x+j,x+j+1); void toggle(double*x, double*y) double t=*x; *x=*y; *y=t; Παρατηρούµε ότι οι συναρτήσεις αυτές χρησιµοποιούν τη συµβολική των δεικτών αντί των πινάκων. Ετσι π.χ. η void input_numbers(double *x,int n) int i; for(i=0; i<=n-1; i++) scanf("%lf",x+i); παίρνει ως όρισµα ένα δείκτη και ένα ακέραιο. Στο κυρίως πρόγραµµα η input_numbers καλείται µε ορίσµατα data και n. Το πρώτο όρισµα είναι το όνοµα του πίνακα των στοιχείων (data), το οποίο γίνεται αντιληπτό ως η θέση µνήµης του µηδενικού στοιχείου του πίνακα. Αυτή η θέση µνήµης εκχωρείται ως τιµή του δείκτη x στο εσωτερικό της input_numbers. Εποµένως, η εντολή for(i=0; i<=n-1; i++) scanf("%lf",x+i); στο εσωτερικό της input_numbers έχει ως αποτέλεσµα ναζητηθούναπότηνοθόνητιµές για τις διαδοχικές θέσεις µνήµης µετά τη θέση µνήµης του x, δηλαδήπρακτικάγιαταστοιχείατουπίνακα. εδοµένου ότι οι τιµές που δίνονται εγγράφονται στις αντίστοιχες θέσεις µνήµης, οι εγγραφές αυτές είναι µόνιµεςκαιγίνονταιαντιληπτές, έξω από την input_numbers ως στοιχεία του πίνακα data, οοποίοςέχει συνδεθεί µε τιςσυγκεκριµένες θέσεις µνήµης. Παρατηρούµε ότι εδώ δίνεται, για πρώτη φορά η εντολή scanf χωρίς το σύµβολο & στη ζητούµενη µεταβλητή. Αυτό συµβαίνει διότι η έκφραση x+i αναφέρεται απευθείας σε µια θέση µνήµης, όπως ακριβώς και η έκφραση &a που χρησιµοποιήσαµε συνήθως στη σύνταξη της εντολής scanf. Οµοίως λειτουργούν και οι υπόλοιπες συναρτήσεις του προγράµµατος. 71