Εθνικό Μετσόβιο Πολυτεχνείο Σχολή Αγρονόµων Τοπογράφων Μηχανικών Προγραµµατιστικές τεχνικές Βασίλειος Βεσκούκης ρ. Ηλεκτρολόγος Μηχανικός & Μηχανικός Υπολογιστών ΕΜΠ v.vescoukis@cs.ntua.gr Ρωµύλος Κορακίτης Αστροφυσικός Αναπλ. Καθηγητής ΕΜΠ romylos@survey.ntua.gr είκτες οµές δεδοµένων στη C++
Ανασκόπηση του µαθήµατος µέχρι τώρα (Σήµερα: 15.5.2003) Απολογισµός 8 προγραµµατισµένα µαθήµατα 3 "απώλειες" 1 µάθηµα σύνδεσης/αναφοράς στο διαγώνισµα του 1ου εξαµήνου 4 µαθήµατα µε νέο περιεχόµενο 2 σειρές ασκήσεων Προγραµµατισµός 5 µαθήµατα µαζί µε το σηµερινό 1 σειρά ασκήσεων Προκειµένου να καλυφθούν Νέες έννοιες (δείκτες, δοµές, κλάσεις, δυναµική παραχώρηση µνήµης, βιβλιοθήκες κλάσεων) Μια µικρή εφαρµογή ΑΤΜ Σύνδεση µε τα επόµενα
είκτες (pointers) Οι µεταβλητές µνήµης δεσµεύουν χώρο στη µνήµη του ΗΥ, χρήσιµο για το πρόγραµµα το οποίο αποθηκεύει σε αυτόν το χώρο κάποια τιµή float x1, y1; // συντεταγµένες του σηµείου 1 float x2, y2; // συντεταγµένες του σηµείου 2 float distance; // απόσταση σηµείων 1-2 κ.ο.κ. Οταν αναφέρουµε το όνοµα µιας µεταβλητής µνήµης, χρησιµοποιούµε την τιµή της distance = sqrt(pow((x1-x2),2)+pow((y1-y2),2)); Η C++ επιλύει κατάλληλα την αναφορά κάθε ονόµατος 2 x1 x2 y1 y2 x1-x2 y1-y2 2 pow1 pow2 pow1+pow2 sqrt distance
είκτες Τι γίνεται µε τις ίδιες τις µεταβλητές µνήµης;;; Πώς διαφοροποιούνται στην πραγµατικότητα η µία από την άλλη; Πώς επιλύει η C++ τις αναφορές στις θέσεις µνήµης (ορατές και µη στο χρήστη); Απάντηση: Μοναδική ταυτοποίηση µιας µεταβλητής από τις υπόλοιπες είναι η διεύθυνση της αρχικής θέσης της µνήµης την οποία καταλαµβάνει Οι λεπτοµέρειες της ανάθεσης µνήµης σε µεταβλητές, της αντιστοίχησης µε ονόµατα κλπ δεν είναι του παρόντος... Τιµήτης µεταβλητής, που ο χρήστης ονοµάζει "y1" ιεύθυνση της µεταβλητής, που ο χρήστης ονοµάζει "y1" x1 y1 x2 y2 2 2 pow1 pow2 pow1+pow2 sqrt distance
είκτες Ενας δείκτης είναι ένας τύπος δεδοµένων της C++ που χρησιµοποιείται για την αποθήκευση διευθύνσεων µνήµης Τύπος int: αποθήκευση ακέραιων Τύπος float: αποθήκευση πραγµατικών αριθµών Τύπος char: αποθήκευση ακεραίων... Τύπος δείκτη: αποθήκευση διευθύνσεων µνήµης Πρέπει να δηλώσουµε τις µεταβλητές τύπου δείκτη, όπως δηλώνουµε τις µεταβλητές τύπου int,, float, char, κλπ, που χρησιµοποιεί το πρόγραµµά µας Με την τιµή του δείκτη δεν έχει (γενικά) νόηµα να κάνουµε αριθµητικές πράξεις, όµως χρησιµοποιείται σε περισσότερο σύνθετες (και ισχυρές) εργασίες x1ptr x1
είκτες: : δήλωση δεικτών ήλωση δεικτών εν υπάρχει όνοµα τύπου "pointer", αντί αυτού... Βάζουµε το "αστεράκι" πριν από το όνοµα της µεταβλητής (προσοχή!) Γενική µορφή δήλωσης: [type] *[variablename] Παραδείγµατα Προσοχή: float x1; // απλές µεταβλητές int i, j, k; int *iptr, *jptr // δείκτες int *iptr, jptr // το όνοµα δεν κάνει τον δείκτη... Επίσης: ο δείκτης NULL δείχνει "στο πουθενά"
είκτες: : χρήση δεικτών Ο τελεστής "&"" Επιστρέφει την διεύθυνση της µεταβλητής µνήµης µπροστά από το όνοµα της οποίας µπαίνει Ετσι, µπορεί να χρησιµοποιηθεί για την απόδοση τιµών σε δείκτες Παράδειγµα int y = 5; int *yptr; yptr = &y; // yptr παίρνει τη διεύθυνση του y yptr δείχνει στο y yptr y 5 yptr 500000 600000 y 600000 5 η τιµή του yptr είναι η διεύθυνση µνήµης του y
είκτες: : χρήση δεικτών Ο τελεστής "*"" Επιστρέφει την τιµή της θέσης µνήµης στην οποία δείχνει ο δείκτης µπροστά από τον οποίο τοποθετείται Προσοχή: είναι χρήσιµος όταν τοποθετείται µπροστά από δείκτη, διαφορετικά είναι επικίνδυνος! Παράδειγµα int y = 5; int *yptr; yptr = &y; // yptr παίρνει τη διεύθυνση του y yptr δείχνει στο y *yptr == y // αληθές y=10; cout<<*yptr; // y παίρνει την τιµή 10 *yptr=12; cout<<y; // τι εκτυπώνεται;;;
είκτες Προσοχή στη χρήση δεικτών int y=5; int *somepointer; somepointer=&y; *somepointer=&y; // σχόλια;;; *somepointer=999; // cout<<somepointer; // τι εκτυπώνεται; cout<<*somepointer; // οµοίως... if (*&y==y) cout<<"xxmmmmmm..."; if (&*somepointer==somepointer) cout<<"again xxmmmmm...";
είκτες και πίνακες Το όνοµα ενός πίνακα στην πραγµατικότητα είναι δείκτης στο πρώτο στοιχείο σ του int apostaseis[100]; int *somepointer; somepointer=apostaseis; // επιτρέπεται... apostaseis[10] == *(somepointer + 10) // και ακόµη χειρότερα... apostaseis[10] == *(apostaseis + 10) Γενικά ισχύει ότι array[n] == *(array+n), καθώς και ότι array[n] == *(aptr + n) όπου aptr είναι δείκτης αρχικοποιηµένος µε aptr=array;
οµές δεδοµένων Μέχρι σήµερα, µοναδικός µηχανισµός αποθήκευσης πληροφοριών σε ένα πρόγραµµα την ώρα που αυτό εκτελείται, ήταν οι µεταβλητές µνήµης οι οποίες: είναι ατοµικές, δηλαδή περιέχει µία τιµή η κάθε µία δηλώνονται πριν τη χρήση τους βασιζόµαστε στην τήρηση ορισµένων παραδοχών κατά την ονοµασία και τη χρήση τους Αυτά είναι καλά, όµως... Ο πραγµατικός κόσµος είναι πιο σύνθετος Πολλές µεταβλητές µνήµης χαρακτηρίζουν µία έννοια του πραγµατικού κόσµου Σηµείο: x, y Γραµµή: x1, y1, x2, y2 Κύκλος: x, y, R, area, perimeter Αν πρέπει να χρησιµοποιούµε αποκλειστικά απλές µεταβλητές µνήµης,, τότε πρέπει να προσέχουµε ιδιαίτερα κατά τη χρήση τους Η ανάγκη για σύνθετες µεταβλητές µνήµης, είναι φανερή
οµές (structures) Ορισµός: Ενα struct είναι ένας τύπος που ορίζεται από τον προγραµµατιστή και περιέχει µέσα του περισσότερες από µία απλές µεταβλητές µνήµης ης, οι οποίες λέγονται πεδία (fields)( ή µέλη (members)( Κάθε structure έχει όνοµα που µετά τη δήλωσή του χρησιµοποιείται ως όνοµα τύπου δηλαδή ορίζουµε µεταβλητές ενός τύπου τον οποίο εµείς δηµιουργήσαµε περιέχει απλές µεταβλητές µνήµης µε διαφορετικά ονόµατα µπορεί να περιέχει αναφορά (δείκτη) στον ίδιο τύπο η δήλωσή του ολοκληρώνεται µε ";" Παράδειγµα struct Line { float x1, y1, x2, y2; int line_id; }; // προσοχή εδώ! βάζουµε και ";" Line L1, L2, L3; // όπως θα ορίζαµε "int" i,j,k;" float a,b,x1;
Χρήση των structures Για ποιο λόγο είναι χρήσιµα τα structures; Για την παράσταση σύνθετων οντοτήτων στα προγράµµατά µας Γραµµή Κύκλος Φυσικό πρόσωπο Αντικείµενο Πράξη Αν και φαίνονται "σύνθετα" είναι ισχυροί µηχανισµοί οργάνωσης των δεδοµένων Παραδείγµατα struct Person { char[20] firstname, lastname; int birthdate, birthmonth, birthyear; char[10] height; float weight; char[20] ColorOfEyes; }; struct Polygon { char[30] description; int koryfes; float x[max_koryfes]; float y[max_koryfes]; float area; };
Χρήση των structures Αναφορά στα πεδία/µέλη: ήλωση του struct και µίας µεταβλητής τέτοιου τύπου όνοµα της µεταβλητής τύπου struct + µια τελεία + όνοµα του πεδίου µέσα στο stuct struct Polygon { char[30] description; int koryfes; float x[max_koryfes]; float y[max_koryfes]; float area; }; Polygon p1; p1.koryfes=4; p1.x[0]=0; p1.y[0]=0; p1.x[1]=1;... p1.area=calc_area(p1.x[], p1.y[], koryfes];
Χρήση των structures Αναφορά στα πεδία/µέλη: ήλωση του struct και τουλάχιστον µίας µεταβλητής τύπου δείκτη στο struct όνοµα της µεταβλητής τύπου δείκτη + το σύµβολο "->" + όνοµα του πεδίου µέσα στο stuct struct Polygon { char[30] description; int koryfes; float x[max_koryfes]; float y[max_koryfes]; float area; }; Polygon *P1; P1->koryfes=4; P1->x[0]=0; P1->y[0]=0; P1->x[1]=1;... P1->area=calc_area(p1->x[], p1->y[], koryfes);
Παράδειγµα χρήσης structures (1) struct Line { float x1, y1, x2, y2; }; float linelength(line L) { float mikos; mikos=sqrt( pow((l.x1-l.x2),2)+pow((l.y1-l.y2),2) ); return mikos; } int main() { Line et1,et2; float len1, len2; cout<<"dose tis syntetagmenes tou et1"<<endl; cout<<"x1="; cin>>et1.x1; cout<<"y1="; cin>>et1.y1; cout<<"x2="; cin>>et1.x2; cout<<"y2="; cin>>et1.y2; cout<<endl<<"tmhma et1: ("<<et1.x1<<", "<<et1.y1<<") - ("<<et1.x2<<", "<<et1.y2<<")"<<endl; } len1=linelength(et1); cout<<"length="<<len1; return 0;
Παράδειγµα χρήσης structures (2) struct Line { float x1, y1, x2, y2; }; float linelength(line L) { float mikos; mikos=sqrt( pow((l.x1-l.x2),2)+pow((l.y1-l.y2),2) ); return mikos; } int main() { Line *et1,*et2; float len1, len2; cout<<"dose tis syntetagmenes tou et1"<<endl; cout<<"x1="; cin>>et1->x1; cout<<"y1="; cin>>et1->y1; cout<<"x2="; cin>>et1->x2; cout<<"y2="; cin>>et1->y2; cout<<endl<<"tmhma et1: ("<<et1->x1<<", "<<et1->y1<<") - ("<<et1->x2<<", "<<et1- >y2<<")"<<endl; } len1=linelength(*et1); cout<<"length="<<len1; return 0;
Γενικά περί structures Γενικές δυσκολίες Πότε τα χρησιµοποιούµε; Τι βάζουµε µέσα; Τι σηµαίνει ότι ορίζουµε ένα δικό µας τύπο; Τεχνικές δυσκολίες Σωστή αναφορά στα πεδία ενός struct Σωστή χρήση των pointers και των συµβόλων *,., ->, & Συντακτικά σφάλµατα κατά τη χρήση των παραπάνω Θέµατα προς συζήτηση Τι είναι τα πεδία του struct; Μπορούµε να έχουµε κρυφά πεδία; Μπορούµε να υπολογίζουµε εξαρτηµένα πεδία εσωτερικά στο struct; struct Polygon { char[30] description; int koryfes; float x[max_koryfes]; float y[max_koryfes]; float area; };