ΔΕΙΚΤΕΣ ΚΑΙ ΔΙΕΥΘΥΝΣΕΙΣ

Σχετικά έγγραφα
Κλήση Συναρτήσεων ΚΛΗΣΗ ΣΥΝΑΡΤΗΣΕΩΝ. Γεώργιος Παπαϊωάννου ( )

Προγραμματισμός Υπολογιστών με C++

Προγραμματισμός Υπολογιστών με C++

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αναφορές Στοίβα και Σωρός Αναφορές-Παράμετροι

ΒΑΣΙΚΟΙ ΤΥΠΟΙ ΚΑΙ ΠΙΝΑΚΕΣ

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αναφορές Στοίβα και Σωρός Μνήμης Αντικείμενα ως ορίσματα

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

Προγραμματισμός Ι (ΗΥ120)

ΚΑΤΑΣΚΕΥΑΣΤΕΣ ΑΝΤΙΓΡΑΦΗΣ

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα Αναφορές

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

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

Προγραμματισμός Υπολογιστών με C++

Αναφορές, είκτες και Αλφαριθμητικά

Αντικειμενοστραφείς Γλώσσες Προγραμματισμού C++ / ROOT

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αναφορές Αντικείμενα ως ορίσματα

Προγραμματισμός Υπολογιστών με C++

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

Τελεστές ΤΕΛΕΣΤΕΣ. Γεώργιος Παπαϊωάννου ( )

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

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

Συναρτήσεις-Διαδικασίες

Διάλεξη 9: Δυναμική Δέσμευση Μνήμης

Διαδικασίες Ι. ΗΥ 134 Εισαγωγή στην Οργάνωση και στον Σχεδιασμό Υπολογιστών Ι. Διάλεξη 4

Προγραμματισμός Υπολογιστών με C++

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αντικείμενα ως ορίσματα Εισαγωγή στις αναφορές

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

Διάλεξη 13η: Δυναμική Διαχείρηση Μνήμης, μέρος 1

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

Βασικά Στοιχεία της Java

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

Στοιχειώδης προγραμματισμός σε C++

Προγραμματισμός Υπολογιστών με C++

ΕΠΛ232 Προγραμματιστικές Τεχνικές και Εργαλεία Δυναμική Δέσμευση Μνήμης (Κεφάλαιο , KNK-2ED)

Η γλώσσα προγραμματισμού C Δυναμική διαχείριση μνήμης

Προγραμματισμός Υπολογιστών με C++

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αναφορές Στοίβα και Σωρός Αναφορές-Παράμετροι String Interning

Εισαγωγή σε αντικειμενοστραφή concepts. Και λίγη C#

Εισαγωγή στη γλώσσα προγραμματισμού C++

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

Υπερφόρτωση τελεστών

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

ΥΠΟΛΟΓΙΣΤΕΣ ΙΙ. Τι περιλαμβάνει μια μεταβλητή; ΔΕΙΚΤΕΣ. Διεύθυνση μεταβλητής. Δείκτης

Πίνακες: μια σύντομη εισαγωγή. Πίνακες χαρακτήρων: τα "Αλφαριθμητικά"

ΕΠΛ233 Βασικά στοιχεία προγραµµατισµού στη JAVA

Προγραμματισμός Υπολογιστών με C++

Chapter 2. Εντολές : Η γλώσσα του υπολογιστή. (συνέχεια) Η διασύνδεση Υλικού και λογισμικού David A. Patterson και John L.

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

Βασικά Στοιχεία της Java

Περιεχόμενα. Πρόλογος... 21

Τι σημαίνει; n Για την αποδοτική δέσμευση δομών δεδομένων μη. n Για την αποφυγή «διαρροών μνήμης» (memory leaks).

Λύβας Χρήστος Αρχική επιµέλεια Πιτροπάκης Νικόλαος και Υφαντόπουλος Νικόλαος

ΠΡΟΗΓΜΕΝΟΙ ΜΙΚΡΟΕΠΕΞΕΡΓΑΣΤΕΣ PROJECT 2: MEMORY MANAGEMENT

10 η Διάλεξη C++ - Πίνακες. Δρ. Χρήστος Δρόσος ΑΕΙ ΠΕΙΡΑΙΑ ΤΤ ΤΜΗΜΑ ΑΥΤΟΜΑΤΙΣΜΟΥ

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

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

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

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

Πληροφορική 2. Γλώσσες Προγραμματισμού

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

Ανάπτυξη και Σχεδίαση Λογισμικού

Συμβολοσειρές ΣΥΜΒΟΛΟΣΕΙΡΕΣ. Γεώργιος Παπαϊωάννου ( )

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αναφορές Στοίβα και Σωρός μνήμης Αντικείμενα παράμετροι String Interning

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

Εισαγωγή ΕΙΣΑΓΩΓΗ. Γεώργιος Παπαϊωάννου ( )

Προγραμματισμός Ι (HY120)

Δυναμική δέσμευση και αποδέσμευση μνήμης. Προγραμματισμός II 1

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

Αντικειμενοστραφείς Γλώσσες Προγραμματισμού C++ / ROOT

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

Περιεχόμενα. Πρόλογος... 17

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

Ανάπτυξη και Σχεδίαση Λογισμικού

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

Chapter 2. Εντολές : Η γλώσσα του υπολογιστή. (συνέχεια) Η διασύνδεση Υλικού και λογισμικού David A. Patterson και John L.

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

Προγραμματισμός Συστημάτων

Προγραμματισμός Υπολογιστών με C++

Εισαγωγή στον Προγραμματισμό

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αναφορές Μέθοδοι που επιστρέφουν αντικείμενα Deep and Shallow Copies

Διάλεξη 5: Δείκτες και Συναρτήσεις

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

«ΕΙΔΙΚΑ ΘΕΜΑΣΑ ΣΟΝ ΠΡΟΓΡΑΜΜΑΣΙΜΟ ΤΠΟΛΟΓΙΣΩΝ» Κεφάλαιο 4: Αντικειμενοςτρεφήσ Προγραμματιςμόσ

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

5. ΣΥΝΑΡΤΗΣΕΙΣ. (Πρόχειρο σχέδιο - Μαθήµατος 3) p Συναρτήσεις, που δεν επιστρέφουν κάποια τιµή

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

! Ο καλός προγραμματισμός επιβάλλει την αποδοτική χρήση. ! Είναι σημαντικό να καταλαβαίνουμε τις διαδικασίες

Η γλώσσα προγραμματισμού C Οι συναρτήσεις στη C (2)

Διαδικασίες ΙI. ΗΥ 134 Εισαγωγή στην Οργάνωση και στον Σχεδιασμό Υπολογιστών Ι. Διάλεξη 5

ΚΑΛΟΥΠΩΜΑΤΑ & ΜΕΤΑΤΡΟΠΕΣ

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

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

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

HY150a Φροντιστήριο 3 24/11/2017

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

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

Περιεχόμενα. Λίγα λόγια για αυτή την έκδοση... 23

Μνήμη Διευθύνσεις Δείκτες. Προγραμματισμός II 1

ΕΠΛ232 Προγραμματιστικές Τεχνικές και Εργαλεία Δείκτες και Συναρτήσεις (Κεφάλαιο 11, KNK-2ED)

Transcript:

ΔΕΙΚΤΕΣ ΚΑΙ ΔΙΕΥΘΥΝΣΕΙΣ Γεώργιος Παπαϊωάννου (2013-14) gepap@aueb.gr

Περιγραφή: Εισαγωγή στους δείκτες Το μοντέλο της μνήμης, σωρός και στοίβα Αναφορές Δείκτες και πίνακες Δέσμευση και αποδέσμευση μνήμης Τελευταία ενημέρωση: Οκτώβριος 2015 Εισαγωγή - 2

Στη C++, έχουμε πρόσβαση στη διεύθυνση μιας μεταβλητής, την οποία και μπορούμε να αποθηκεύσουμε ως μεταβλητή κι αυτή (δείκτης): int a = 10; int * p_a = &a; *p_a = 12; Μνήμη (bytes):. &: Η διεύθυνση στη μνήμη μιας μεταβλητής int *: Μεταβλητή τύπου Δείκτη σε int (διεύθυνση μιας ποσότητας τύπου int) *p_a: Το περιεχόμενο μιας διεύθυνσης, ή αλλιώς τα δεδομένα που «δείχνει» ο δείκτης a (10) p_a (132) 132 133 134 135 136.. 0x00 0x00 0x00 0x0A 0x00 0x00 0x00 0x88 3

Για τους ίδιους λόγους που στη Java χρειάζομαι τις αναφορές (είναι το ίδιο πράγμα!). Ποιοι είναι αυτοί; Για να έχω τυχαία προσπέλαση στη μνήμη Για να έχω ορατότητα σε κοινόχρηστα δεδομένα χωρίς να τα αντιγράφω συνέχεια Για να διαχειρίζομαι αντικείμενα και συναρτήσεις σε χαμηλό επίπεδο (π.χ. δείκτες σε συναρτήσεις!) 4

int i = 5; cout << i; // Τυπώνει 5. cout << &i; // Τυπώνει τη διεύθυνση της i: 0x85AF. int* p = &i; // Δείκτης σε ακέραιο. cout << p; // Τυπώνει 0x85AF. cout << *p; // Τυπώνει 5. p i 0x85AF i 5 p *p = 6; // Αλλάζει το i. 0x85B3 0x85AF cout << i; // Τυπώνει 6. int j = 10; p = &j; // Τώρα το p δείχνει στο j. cout << *p; // Τυπώνει 10. p j 5

Ο δείκτες περιέχουν διευθύνσεις και άρα είναι φυσικοί αριθμοί! Άρα μπορώ να κάνω: Βασικές πράξεις μεταξύ αριθμών και δεικτών ή μεταξύ δεικτών (όποιες έχουν νόημα): +, -, +=, -=, ++, -- Συγκρίσεις δεικτών (δηλ. συγκρίσεις διευθύνσεων) Ο τύπος της μεταβλητής που δείχνει ένας pointer καθορίζει και το increment των αριθμητικών πράξεων: int a = 10; int * p_a = &a; p_a += 1; // ΔΕΝ ανεβαίνει κατά ένα byte, αλλά κατά ένα int (4 bytes) 6

Η άκυρη τιμή δείκτη είναι η μηδενική διεύθυνση: 0 \0 NULL nullptr (C++11) float * a = NULL; 7

Οι πίνακες είναι συνεχόμενες θέσεις μνήμης Ένας πίνακας Α, Ν στοιχείων ξεκινά από μια διεύθυνση βάσης (base pointer): &Α[0] == A (η διεύθυνση του πρώτου στοιχείου) Εκτείνεται για N X sizeof(type) bytes Ο τελεστής [i] είναι ισοδύναμος με «το περιεχόμενο στη θέση i»,δηλαδή: Α[i] = *(A+i) Α==&Α[0] sizeof(type) 0 1 2 N 8

int arr[10]; bool b = (arr[2] == * (arr+2)); // true arr = arr + 1; Αυτό δεν είναι επιτρεπτό γιατί εδώ Ο arr είναι δηλωμένος ως σταθερός (arr[10]), άρα ναι μεν έχω πρόσβαση στη διεύθυνση αρχής, αλλά δε μπορώ να την αλλάξω 9

char buffer[80] = σκουλικομερμιγκότρυπα ; char *sub = buffer+8; cout << sub; // μερμιγκότρυπα for (int i = 0; i<=6; i++) buffer sub σ κ ο υ λ ι κ ο μ ε ρ μ ι γ κ ο τ ρ υ π α \0.. *(buffer+8+i) = *(buffer+16+i); // buffer[8+i] = buffer[16+i]; cout << buffer; // σκουλικοτρυπα buffer σ κ ο υ λ ι κ ο τ ρ υ π α \0 κ ο τ ρ υ π α \0.. 10

int arr[] = {1, 2, 3; int* arr = new int[3]; int* arr = {1, 2, 3; int arr[] = new int[3]; // Στατικός πίνακας. // Δυναμικός πίνακας. // Δεν επιτρέπεται. // Δεν επιτρέπεται. char s[] = "test"; // Στατικός πίνακας χαρακτήρων. char* s = "test"; // ΟΚ! Για συμβατότητα με τη C. char* s1; // Οι s και s1 θα δείχνουν στην s1 = "test"; // 1η θέση του πίνακα // χαρακτήρων "test". 11

char s[] = "test"; Στην πρώτη σειρά, η συμβολοσειρά αποθηκεύεται σε έναν πίνακα που εκχωρείται γι αυτό το σκοπό και που έχει ακριβώς το μέγεθος της. Μπορώ να έχω πρόσβαση και να αλλάξω τα περιεχόμενα του s Δε μπορώ να μεταβάλλω την τιμή του s (const char *) s t e s t 0 char* s = "test"; s t e s t 0 Στη δεύτερη σειρά, δημιουργείται ένας σταθερός πίνακας χαρακτήρων (readonly) και ένας δείκτης που απλά μας δείχνει στην πρώτη θέση του Δεν επιτρέπεται να μεταβάλω τα περιεχόμενα του test Μπορώ να μεταβάλω το δείκτη 12

Η sizeof μας δίνει το μέγεθος μιας μεταβλητής στη μνήμη Άρα για δείκτες μας δίνει πόσα bytes καταλαμβάνει η αναπαράσταση μιας διεύθυνσης: Για 32bit αρχιτεκτονικές είναι 4 Για 64bit αρχιτεκτονικές είναι 8 Παρατήρηση: char s[] = "test"; sizeof(s) == 5 // Το μέγεθος ενός σταθερού πίνακα 13

Πως μπορώ να φτιάξω έναν πίνακα από δείκτες; Πως μπορώ να έχω πρόσβαση στα στοιχεία του; Ποια χρήση έχει κάτι τέτοιο; 14

Συχνά, χρειάζεται να περνάμε δεδομένα σε περιοχές μνήμης χωρίς να ξέρουμε τον ακριβή τύπο τους Πότε; Όταν διαβάζουμε δεδομένα που τα προσπερνάμε ή τα μεταβιβάζουμε χωρίς να μας απασχολεί το περιεχόμενό τους (π.χ.network package data) Όταν θέλουμε να ορίσουμε παραμέτρους σε συναρτήσεις ή μεθόδους χωρίς να ξέρουμε τον ακριβή τύπο που θα περαστεί, αλλά μπορούμε να τον συμπεράνουμε εσωτερικά ( π.χ. void ThreadProcess(void *pdata) ) 15

Επειδή δεν έχει ακριβή τύπο το void *, οποιαδήποτε πράξη πάνω σε έναν τέτοιο δείκτη γίνεται με μονάδα το byte Τη μνήμη που δείχνει ένας δείκτης μπορώ να την καλουπώσω σε οτιδήποτε! char a[4] = {1,2,3,4; void * p_a = a; int b = *((int*)p_a); // copy // b == 67305985 1 2 3 4 0x01 0x02 0x03 0x04 67305985 16

C++ Ο δείκτης είναι ισοδύναμος με τη διεύθυνση στη μνήμη μιας μεταβλητής και χρησιμεύει ως αναφορά σε αυτή, διαχωρίζοντάς τη από το περιεχόμενό της Java Υπάρχει μόνο η έννοια της «αναφοράς» σε αντικείμενα, που ισοδυναμεί με ένα δείκτη σε αντικείμενα στη C++ Αν δεν ξέρουμε τον τύπο ενός ορίσματος όταν δηλώνουμε μια συνάρτηση/μέθοδο, περνάμε απλά μια διεύθυνση μεταβλητής: void * Αν δεν ξέρουμε τον τύπο ενός ορίσματος όταν δηλώνουμε μια μέθοδο, περνάμε απλά μια αναφορά σε Object Εισαγωγή - 17

Κάθε πρόγραμμα όταν εκτελείται, δεσμεύει μνήμη σε δύο φάσεις: Στατικά (με την εκκίνηση) Δυναμικά (κατά την εκτέλεσή του) Στην αρχικοποίηση του προγράμματος δεσμεύεται κατευθείαν ο χώρος που είναι γνωστός από πριν: Χώρος για τον δυαδικό κώδικα μηχανής Χώρος για στατικές μεταβλητές Χώρος για την υπογραφή των κλάσεων κλπ Όλες οι μεταβλητές δεσμεύουν χώρο δυναμικά! 18

Κάθε πρόγραμμα δεσμεύει 5 τύπους χώρου μνήμης: Χώρος εκτελέσιμου δυαδικού κώδικα μηχανής (στατικός). Λέγεται code segment ή text segment (παρότι δεν είναι κείμενο!) Αρχικοποιημένος χώρος δεδομένων (data segment). Περιέχει στατικές μεταβλητές, υπογραφές κλασεων, global μεταβλητές (στατικός) Χώρος μη αρχικοποιημένων δεδομένων (BSS) Σωρός (heap), για δυναμική δέσμευση μνήμης (π.χ. με new) Στοίβα (call stack) για τις αυτόματες (τοπικές) μεταβλητές και τις παραμέτρους κλήσης συναρτήσεων 19

Ένα εκτελέσιμο αρχείο περιλαμβάνει, εκ κατασκευής από το μεταγλωττιστή και συνδέτη, όλη τη δυαδική πληροφορία του code segment και data segment Η δυαδική αναπαράσταση φορτώνεται στο χώρο (virtual memory space) που εκχωρείται από το λειτουργικό, ως έχει Η δυναμική περιοχή μνήμης δεν αποθηκεύεται στο αρχείο προφανώς, αλλά καθορίζεται και δεσμεύεται καθώς εκτελείται ο κώδικας 20

Μέγιστη εικονική διεύθυνση (κάτι μεγάλο, π.χ. 2 39 ) Environment variables Η στοίβα μεγαλώνει προς τα κάτω (μικρότερες διευθύνσεις x86 αρχιτεκτονικές) Ο σωρός δεσμεύεται κι αυξάνει προς τα πάνω (προς μεγαλύτερες διευθύνσεις μνήμης) Call stack Heap Uninitialized (global) data Initialized global data Εικονική μνήμη 0 Code segment 21

Κάθε φορά που καλείται μια συνάρτηση (ή μέθοδος), ακόμα και η main(), χρησιμοποιείται χώρος στην περιοχή της στοίβας κλήσεων συναρτήσεων (call stack ή σκέτο stack) Ο χώρος μνήμης της στοίβας χρησιμοποιείται αποκλειστικά γι αυτή τη χρήση Έχει πολύ απλό μηχανισμό (στοίβας ) για τη δέσμευση και αποδέσμευση (rewind) χώρου Αποθηκεύονται: Όλες οι τοπικές μεταβλητές μιας συνάρτησης Οι παράμετροι που περάστηκαν Το σημείο εξόδου και επαναφοράς στην καλούσα συνάρτηση (θέση μνήμης) 22

C++ Ο κώδικάς μου παράγει εκτελέσιμο που φορτώνεται όπως στο παραπάνω σχήμα και εκτελείται απευθείας πάνω στο λειτουργικό σύστημα Java Ο κώδικάς μου είναι στην ουσία τα «δεδομένα» ενός άλλου προγράμματος (JVM), γραμμένου σε C++ ( :D ) που αυτό τρέχει στο λειτουργικό μου σύστημα (*) H Java υλοποιεί στο χώρο μνήμης δεδομένων της, heap, stacks και code segments για τα threads των Java προγραμμάτων που εκτελούνται (*) http://www.artima.com/insidejvm/ed2/jvm.html Εισαγωγή - 23

static int s_f = 3.1f; float Foo(float a, float b) { float c = a+b; return c*c; Μετά το φόρτωμα του εκτελέσιμου exe path:= c:\temp\test.exe args = test.exe int main() { float data[] = {.1f,.2f; data[0] = Foo(data[1], s_f); cout << data[0]; return 0; Μεταγλώττιση, σύνδεση και φόρτωμα s_f := 3.1 cout main() machine code Foo() machine code osteam::<<() machine code 24

static int s_f = 3.1f; float Foo(float a, float b) { float c = a+b; return c*c; Έναρξη εκτέλεσης exe path:= c:\temp\test.exe args = test.exe int main() { float data[] = {.1f,.2f; data[0] = Foo(data[1], s_f); cout << data[0]; return 0; PC s_f := 3.1 cout main() machine code Foo() machine code osteam::<<() machine code 25

static int s_f = 3.1f; float Foo(float a, float b) { float c = a+b; return c*c; SP Κλήση της main() exe path:= c:\temp\test.exe args = test.exe Παράμετροι της main() (0 bytes) Διεύθυνση επιστροφής (στο OS) Διεύθυνση καλούντος (OS) data[2]:= {.1,.2 (8 bytes) int main() { float data[] = {.1f,.2f; data[0] = Foo(data[1], s_f); cout << data[0]; return 0; PC s_f := 3.1 cout main() machine code Foo() machine code osteam::<<() machine code 26

static int s_f = 3.1f; float Foo(float a, float b) { float c = a+b; return c*c; int main() { float data[] = {.1f,.2f; data[0] = Foo(data[1], s_f); cout << data[0]; return 0; SP PC Κλήση της Foo(float,floar) exe path:= c:\temp\test.exe args = test.exe Παράμετροι της main() (0 bytes) Διεύθυνση επιστροφής (στο OS) Διεύθυνση καλούντος (OS) data[2]:= {.1,.2 (8 bytes) Παράμετροι της Foo() (8 bytes) Διεύθυνση επιστροφής (&(data[0])) Διεύθυνση καλούντος (παλιός PC) c (4 bytes) s_f := 3.1 cout main() machine code Foo() machine code osteam::<<() machine code 27

static int s_f = 3.1f; float Foo(float a, float b) { float c = a+b; return c*c; SP Επιστροφή από τη Foo(float,float) exe path:= c:\temp\test.exe args = test.exe Παράμετροι της main() (0 bytes) Διεύθυνση επιστροφής (στο OS) Διεύθυνση καλούντος (OS) data[2]:= {.1,.2 (8 bytes) int main() { float data[] = {.1f,.2f; data[0] = Foo(data[1], s_f); cout << data[0]; return 0; PC s_f := 3.1 cout main() machine code Foo() machine code osteam::<<() machine code 28

static int s_f = 3.1f; float Foo(float a, float b) { float c = a+b; return c*c; int main() { float data[] = {.1f,.2f; data[0] = Foo(data[1], s_f); cout << data[0]; return 0; SP PC Κλήση της ostream::<<(float) exe path:= c:\temp\test.exe args = test.exe Παράμετροι της main() (0 bytes) Διεύθυνση επιστροφής (στο OS) Διεύθυνση καλούντος (OS) data[2]:= {.1,.2 (8 bytes) Παράμετροι της <<() (4 bytes) Διεύθυνση επιστροφής (-) Διεύθυνση καλούντος (παλιός PC) Τοπικές μεταβλητές της cout s_f := 3.1 cout main() machine code Foo() machine code osteam::<<() machine code 29

static int s_f = 3.1f; float Foo(float a, float b) { float c = a+b; return c*c; SP Επιστροφή από τη <<(float) exe path:= c:\temp\test.exe args = test.exe Παράμετροι της main() (0 bytes) Διεύθυνση επιστροφής (στο OS) Διεύθυνση καλούντος (OS) data[2]:= {.1,.2 (8 bytes) int main() { float data[] = {.1f,.2f; data[0] = Foo(data[1], s_f); cout << data[0]; return 0; PC s_f := 3.1 cout main() machine code Foo() machine code osteam::<<() machine code 30

static int s_f = 3.1f; Επιστροφή στην καλούσα διεργασία exe path:= c:\temp\test.exe args = test.exe float Foo(float a, float b) { float c = a+b; return c*c; SP int main() { float data[] = {.1f,.2f; data[0] = Foo(data[1], s_f); cout << data[0]; return 0; PC s_f := 3.1 cout main() machine code Foo() machine code osteam::<<() machine code 31

Προσοχή: Για λόγους ταχύτητας, στη C++ και στη C δεν αποδίδονται τυπικές τιμές κατά τη δήλωση μεταβλητών. Εμείς είμαστε υπεύθυνοι για την απόδοση αρχικής τιμής int c; cout << c; // 3435973836 (0xcccccccc) : σκουπίδια Γιατί; Αν η μεταβλητή θα υποδεχθεί ένα αποτέλεσμα ή μία ανάθεση αργότερα δεν υπάρχει λόγος να εκτελεστεί η αρχικοποίηση της σε μια τιμή που ποτέ δε θα χρησιμοποιηθεί 32

Είδαμε ότι οι τοπικές μεταβλητές δεσμεύονται σε κάθε stack frame κλήσης μιας συνάρτησης. Όταν η συνάρτηση επιστρέφει, τα δεδομένα αυτά χάνονται Συχνά δεν είμαστε σε θέση να προσδιορίσουμε από την αρχή το μέγεθος ή και τον ακριβή τύπο πινάκων και δεδομένων (βλ. πίνακες και κληρονομικότητα) Θέλουμε να δεσμεύσουμε μεταβλητές σε χώρο που να έχουν πρόσβαση πολλές συναρτήσεις/μέθοδοι/νήματα Θέλουμε να δημιουργήσουμε μη συνεκτικά δεδομένα στη μνήμη (συνδεδεμένες λίστες, όχι πίνακες) Θέλουμε να δεσμεύσουμε μεγάλη ποσότητα μνήμης, που δε γίνεται να μπει σε ένα frame (stack overflow) 33

Είναι το διάστημα μνήμης που το πρόγραμμά μας μπορεί να δεσμεύσει χώρο για δεδομένα δυναμικά Είναι ξεχωριστός από το χώρο των αυτόματων μεταβλητών (call stack) Η δέσμευση χώρου γίνεται με την εντολή new στη C++ (όπως και στη Java) Τη μνήμη που δεσμεύουμε, πρέπει να την αποδεσμεύουμε εμείς με την εντολή delete ΔΕΝ υπάρχει garbage collection στη C++ (*) (*) Υπάρχει υποστήριξη μέσω βιβλιοθηκών (π.χ. auto_ptr) για δείκτες που καταστρέφονται μόνοι τους, αλλά αυτό έχει πολυπλοκότητα και κόστος (όπως και στη Java!) 34

float* fp = new float; // Δημιουργία νέου χώρου // αποθήκευσης για μία τιμή float *fp = 2.0F; // Ανάθεση τιμής float στο // περιεχόμενο του fp cout << fp << endl // Τυπώνει 0xFF10 (διεύθυνση) << *fp << endl; // Τυπώνει 2 (τιμή) delete fp; // Αποδέσμευση του χώρου στον // οποίο δείχνει o fp Σε αυτή την περίπτωση ο δείκτης δεν δείχνει στο χώρο μιας άλλης μεταβλητής, αλλά σε χώρο που δημιουργήσαμε με new 35

Για απλούς τύπους (float, int κλπ), δεσμεύουν και αποδεσμεύουν μνήμη Για κλάσεις (με αυτή τη σειρά): new: Δεσμεύει μνήμη, όση χρειάζεται για να αποθηκευτεί ένα στιγμιότυπο Καλεί τον κατασκευαστή της κλάσης delete: Καλεί τον καταστροφέα της κλάσης Αποδεσμεύει τη μνήμη που είχε εκχωρηθεί 36

Για να δεσμεύσουμε μια ακολουθία από ίδιου τύπου δεδομένα χρησιμοποιούμε τον τελεστή new[]: float array* = new float[num]; Και για να διαγράψουμε τον πίνακα στοιχείων αυτόν χρησιμοποιούμε την delete[]: delete[] array; 37

int a[5]; // Στατική καταχώριση χώρου για πίνακα. int* b = new int[5]; // Δυναμική καταχώριση χώρου για πίνακα. a[4] = 10; b[4] = 10; *a = 2; // Ισοδύναμο του a[0] = 2. *b = 2; // Ισοδύναμο του b[0] = 2. *(a + 1) = 4; // Ισοδύναμο του a[1] = 4. *(b + 1) = 4; // Ισοδύναμο του b[1] = 4. b = a; // Δεν είναι λάθος, χάσαμε όμως τη διεύθυνση του // δυναμικού πίνακα. Πώς θα κάνουμε delete[]; int* c = a; // Τα ονόματα πινάκων είναι ουσιαστικά δείκτες. a = b; // Τα ονόματα πινάκων είναι δείκτες που δεν // επιτρέπεται να δείξουν αλλού. 38

char * CreateData(int num) { char * data = new char[num]; for (int i=0; i<num; i++) data[i] = '\0'; return data; Stack exe path:= c:\temp\test.exe args = test.exe Παράμετροι της main() (0 bytes) Διεύθυνση επιστροφής (στο OS) Διεύθυνση καλούντος (OS) char *:=?? (4 bytes) int main() { char * str = CreateData(10); str[0] = A ; cout << str; delete[] str; return 0; 39

char * CreateData(int num) { char * data = new char[num]; for (int i=0; i<num; i++) data[i] = '\0'; return data; int main() { char * str = CreateData(10); str[0] = A ; cout << str; delete[] str; return 0; Stack exe path:= c:\temp\test.exe args = test.exe Παράμετροι της main() (0 bytes) Διεύθυνση επιστροφής (στο OS) Διεύθυνση καλούντος (OS) str :=?? (4 bytes) Παράμετροι της CreateData() (4 bytes) Διεύθυνση επιστροφής (&str) Διεύθυνση καλούντος (PC main) data :=?? (4 bytes) 40

char * CreateData(int num) { char * data = new char[num]; for (int i=0; i<num; i++) data[i] = '\0'; return data; int main() { char * str = CreateData(10); str[0] = A ; cout << str; delete[] str; return 0; data Stack exe path:= c:\temp\test.exe args = test.exe Παράμετροι της main() (0 bytes) Διεύθυνση επιστροφής (στο OS) Διεύθυνση καλούντος (OS) str :=?? (4 bytes) Παράμετροι της CreateData() (4 bytes) Διεύθυνση επιστροφής (&str) Διεύθυνση καλούντος (PC main) data := 0x00560404 (4 bytes) 0x00560404 Heap $^ &*^&G>Γ 41

char * CreateData(int num) { char * data = new char[num]; for (int i=0; i<num; i++) data[i] = '\0'; return data; int main() { char * str = CreateData(10); str[0] = A ; cout << str; delete[] str; return 0; data Stack exe path:= c:\temp\test.exe args = test.exe Παράμετροι της main() (0 bytes) Διεύθυνση επιστροφής (στο OS) Διεύθυνση καλούντος (OS) str :=?? (4 bytes) Παράμετροι της CreateData() (4 bytes) Διεύθυνση επιστροφής (&str) Διεύθυνση καλούντος (PC main) data := 0x00560404 (4 bytes) 0x00560404 Heap \0\0\0\0\0\0\0\0\0\0 42

char * CreateData(int num) { char * data = new char[num]; for (int i=0; i<num; i++) data[i] = '\0'; return data; int main() { char * str = CreateData(10); str[0] = A ; cout << str; delete[] str; return 0; str data Stack exe path:= c:\temp\test.exe args = test.exe Παράμετροι της main() (0 bytes) Διεύθυνση επιστροφής (στο OS) Διεύθυνση καλούντος (OS) str := 0x00560404 (4 bytes) Παράμετροι της CreateData() (4 bytes) Διεύθυνση επιστροφής (&str) Διεύθυνση καλούντος (PC main) data := 0x00560404 (4 bytes) 0x00560404 Heap \0\0\0\0\0\0\0\0\0\0 43

char * CreateData(int num) { char * data = new char[num]; for (int i=0; i<num; i++) data[i] = '\0'; return data; Stack exe path:= c:\temp\test.exe args = test.exe Παράμετροι της main() (0 bytes) Διεύθυνση επιστροφής (στο OS) Διεύθυνση καλούντος (OS) str := 0x00560404 (4 bytes) int main() { char * str = CreateData(10); str[0] = A ; cout << str; delete[] str; return 0; str 0x00560404 Heap \0\0\0\0\0\0\0\0\0\0 44

char * CreateData(int num) { char * data = new char[num]; for (int i=0; i<num; i++) data[i] = '\0'; return data; Stack exe path:= c:\temp\test.exe args = test.exe Παράμετροι της main() (0 bytes) Διεύθυνση επιστροφής (στο OS) Διεύθυνση καλούντος (OS) str := 0x00560404 (4 bytes) int main() { char * str = CreateData(10); str[0] = A ; cout << str; delete[] str; return 0; str 0x00560404 Heap A\0\0\0\0\0\0\0\0\0 45

char * CreateData(int num) { char * data = new char[num]; for (int i=0; i<num; i++) data[i] = '\0'; return data; Stack exe path:= c:\temp\test.exe args = test.exe Παράμετροι της main() (0 bytes) Διεύθυνση επιστροφής (στο OS) Διεύθυνση καλούντος (OS) str :=???? (4 bytes) int main() { char * str = CreateData(10); str[0] = A ; cout << str; delete[] str; return 0; Heap Δεν ξεχνάμε να καλέσουμε τη delete, αλλιώς η μνήμη δε θα αποδεσμευθεί! (Memory leak) 46

Η μνήμη των αυτόματων μεταβλητών, δηλαδή των τοπικών μεταβλητών μέσα στο σώμα μιας συνάρτησης ή μεθόδου, διαγράφεται μόνη της με την επιστροφή της συνάρτησης (επιστρέφει ο Stack Pointer στην παλιά του τιμή) Η μνήμη που δεσμεύουμε στο σωρό παραμένει δεσμευμένη και μετά την επιστροφή της συνάρτησης 47

#include <iostream> using namespace std; int main() { unsigned howmany; cin >> howmany; int* storage = new int[howmany]; for(unsigned i = 0; i < howmany; i++) { cin >> storage[i]; for(unsigned i = 0; i < howmany; i++) { cout << i << ": " << storage[i] << endl; delete[] storage; 48

const int i2 = 10; X int* p2 = &i2; // Θα μπορούσα να αλλάξω το i2 // μέσω του p2, με *p2 = 20. const int* p3 = &i2; // OK. Υπόσχομαι να μην αλλάξω // αυτό στο οποίο δείχνει ο p3. cout << *p3; // OK. Τυπώνει 10. X *p3 = 20; // Δεν επιτρέπεται. Δείκτης σε const. int i1 = 10; // Το i1 δεν είναι σταθερά. const int* p4 = &i1; // Επιτρέπεται, αλλά δεν μπορώ να // αλλάξω το i1 μέσω του p4. i1 = 20; // OK. X *p4 = 20; // Δεν επιτρέπεται. 49

int j1 = 10, j2 = 20; int* const p5 = &j1; // Σταθερός δείκτης στο j1. *p5 = 30; // ΟΚ, αλλάζει το j1. X p5 = &j2; // Ο p5 δεν μπορεί να δείξει αλλού. const int* const p6 = &j1; // Σταθερός δείκτης σε // σταθερά. X p6 = &j2; // Ο p6 είναι σταθερός: δεν // μπορεί να δείξει αλλού. X *p6 = 30; // Ο p6 δείχνει σε σταθερά: // δεν μπορεί να αλλάξει τα // περιεχόμενα της θέσης // μνήμης στην οποία δείχνει. 50

Η αντίστοιχη δέσμευση / αποδέσμευση μνήμης μπορεί να γίνει με τις συναρτήσεις της C: void * malloc(num_bytes) και void free(void *): float data = (float*) malloc( 10*sizeof(float) ); free (data); ΠΟΤΕ δεν αναμιγνύουμε τη new με την free και την malloc με την delete (είναι σφάλμα) 51

Ένας δείκτης (διεύθυνση), ως μεταβλητή απαιτεί κάποιο χώρο αποθήκευσης, π.χ. 32bits Το μέγεθος του δείκτη μπορούμε να το πάρουμε κι αυτό με τη sizeof: π.χ. sizeof(float*) ΔΕΝ πρέπει να υποθέτουμε μόνοι μας ποτέ πόσο είναι το μέγεθος του pointer γιατί αλλάζει μεταξύ αρχιτεκτονικών (π.χ. στη x64 είναι 64bits) 52