270 likes | 415 Views
ΗΥ - 150 Προγραμματισμός. Δυναμική Διαχείριση Μνήμης (1/2). Δυναμική Διαχείριση Μνήμης. Μέχρι τώρα στατική ανάθεση και δέσμευση μνήμης Ζητούσαμε στο π ρόγραμμά μας τη μέγιστη μνήμη π ου μ π ορεί να χρειαζόμασταν #define MAX_SIZE 10000 int array[MAX_SIZE];
E N D
ΗΥ-150Προγραμματισμός ΔυναμικήΔιαχείρισηΜνήμης (1/2)
ΔυναμικήΔιαχείρισηΜνήμης • Μέχριτώραστατικήανάθεσηκαιδέσμευσημνήμης • Ζητούσαμεστο πρόγραμμάμαςτημέγιστημνήμη πουμπορείναχρειαζόμασταν • #define MAX_SIZE 10000 • int array[MAX_SIZE]; • Μειονέκτημα : Υπάρχουνεφαρμογές πουτοMAX_SIZEείναιάγνωστοοπότεαναγκαζόμαστεναδηλώνουμεμεγάλεςτιμέςκαιναχάνουμεκαισεμνήμηκαισεταχύτητα • Δυναμικήκατανομήμνήμης (dynamic memory allocation) • Αίτησηαπότολειτουργικόναμας παραχωρήσειμνήμη • Ειδοποίησηστολειτουργικόότιδενχρειαζόμαστε πιαέναμέροςμνήμης • Ό,τιδεσμεύετε πρέπεινααποδεσμεύετε
ΚατηγορίεςΜνήμηςτουΠρογράμματοςΚατηγορίεςΜνήμηςτουΠρογράμματος • Μνήμηαποθήκευσηςτουκώδικατου προγράμματος • Μνήμηαποθήκευσηςτωνμεταβλητώνενός προγράμματος πουδεσμεύονταιμεστατικήήαυτόματηδιαχείρισημνήμης • Αυτήημνήμηονομάζεταιστοίβα(stack) • Μνήμηαποθήκευσηςτωνμεταβλητώνενός προγράμματος πουδεσμεύονταιμεδυναμικήδιαχείρισημνήμης • Αυτήημνήμηονομάζεταισωρός (heap)
ΣυναρτήσειςΔυναμικήςΔιαχείρισηςΜνήμηςΣυναρτήσειςΔυναμικήςΔιαχείρισηςΜνήμης • Βρίσκονταιστηνstdlib.h • void *malloc( size_t numBytes); • ΔεσμεύειnumBytes αριθμόαπόδιαδοχικάBytes καιεπιστρέφειέναδείκτηστηναρχήτηςδεσμευμένηςθέσημνήμης • ΕπιστρέφειNULL ανδενμπορείναδεσμεύσειάλλημνήμη int *ptr = (int *) malloc (100*sizeof(int));
ΣυναρτήσειςΔυναμικήςΔιαχείρισηςΜνήμηςΣυναρτήσειςΔυναμικήςΔιαχείρισηςΜνήμης • void *calloc(size_t nelements, size_t bytes); • Δεσμεύειnelements αριθμόαπόδιαδοχικάαντικείμενατοκαθέναμεγέθουςbytes καιεπιστρέφειέναδείκτηστηναρχήτηςδεσμευμένηςθέσημνήμης • ΕπιστρέφειNULL ανδενμπορείναδεσμεύσειάλλημνήμη • Αρχικοποιείτα περιεχόμενατηςδεσμευμένηςμνήμηςστο 0 • Παρόμοιαμετηνmalloc • Διαφορά 1: λίγοδιαφορετικήκλήση • Διαφορά 2: αρχικοποίησητων περιεχομένωνμε 0 int *ptr = (int *) calloc (100, sizeof(int));
ΣυναρτήσειςΔυναμικήςΔιαχείρισηςΜνήμηςΣυναρτήσειςΔυναμικήςΔιαχείρισηςΜνήμης • void free(void *pointer); • Αποδεσμεύειτημνήμη πουέχειδεσμευτείμεμίααπότις προηγούμενεςκλήσειςστηνmalloc, calloc ήrealloc • Δεν πρέπεινααποδεσμεύουμε πάνωαπόμιαφοράτηνίδιαμνήμη ptr= (int *)malloc (100*sizeof(int)); free(ptr); // Αυτόείναιοκμέχριεδώ free(ptr); // Αποδεσμεύουμετηνίδιαμνήμη // δυοφορές! ΛΑΘΟΣ
ΣυναρτήσειςΔυναμικήςΔιαχείρισηςΜνήμηςΣυναρτήσειςΔυναμικήςΔιαχείρισηςΜνήμης • void *realloc(void *ptr, size_t size); • Επέκτασημνήμης • Οδείκτης*ptr πρέπειναδείχνεισεμνήμηήδηδεσμευμένημετηνmalloc ήcalloc • ptr= (int *)malloc (100*sizeof(int)); • Επαναδέσμευσημνήμηςμεάλλομέγεθος • ptr= (int *)realloc(ptr, 200*sizeof(int)); • Τα προηγούμενα περιεχόμενατηςδεσμευμένηςμνήμηςδιατηρούνται (τουλάχιστονόσαχωρούνστηνκαινούργια) • Ηκαινούργιαμνήμημπορείναείναισεάλληδιεύθυνσηανδενυπήρχεαρκετάμεγάλοσυνεχόμενοblockαπόελεύθερημνήμηγιαδέσμευση. Σεαυτήντην περίπτωσηγίνεταιμεταφοράτωνδεδομένωνκαιαποδεσμεύεταιημνήμηptr, σανναγίνεταιδηλαδήμιαfree(ptr)
Οτύποςvoid* • Γενικόςδείκτης • Κατάτηνεκτέλεσητου προγράμματοςαποφασίζετεοτύποςτου(char *, int *, float *, double *, struct my_struct *, …) • Ογενικόςδείκτηςμαςδίνειτηδυνατότηταναορίζουμεσυναρτήσεις πουμαςεπιστρέφουμεχώροστημνήμηχωρίςναμαςενδιαφέρειοτύπος! Π.χ. malloc • ΠΡΟΣΟΧΗ: σεσυναρτήσειςείναιτελείωςδιαφορετικό 1. void f() // ησυνάρτησηΔΕΝεπιστρέφειτίποτα 2. void *f() //επιστρέφειέναδείκτη πουο προγραμματιστής πρέπεινα //αποσαφηνίσειτοντύποτουμέσαστηνσυνάρτηση
ΔυναμικοίΠίνακες • Πίνακες πουτομέγεθοςτουςκαθορίζεταικατάτηνεκτέλεσητου προγράμματος ( σεrun-time). • Δυνατότητα προσαρμογήςστιςεκάστοτεαπαιτήσειςχωρίςανάγκη περιορισμού. • int *x = (int *)malloc( 10 * sizeof(int)); x
Πρόβλημα 1: ΑποτυγχάνειηΔέσμευση • Πρέπειναγίνεταιπάνταέλεγχοςεπιστροφήςτηςαπαραίτητηςμνήμης!! • Είναιευθύνητου προγραμματιστήκαιστοιχείοσωστού προγραμματισμού! • ΟισυναρτήσειςδυναμικήςδέσμευσηςεπιστρέφουνNULL όταναποτυγχάνουν, τοοποίομπορούμεναεξετάσουμεκαιναδιακόψουμετηνεκτέλεσητου προγράμματοςανχρειάζεται int *p = (int *)malloc(10000*sizeof(int)); if (p == NULL) { printf( “No Memory Available\n”); exit(0); }
Πρόβλημα 2: ΔιαρροήΜνήμης • Απελευθέρωση • Πρέπειναελευθερώνουμετηνμνήμηότανδεντηνχρειαζόμαστε (μετηνfree) • Δενελευθερώνουμε ποτέμνήμη πουδενέχειαποκτηθείδυναμικά (λ.χ. μεmalloc) • Ανδενελευθερώσουμεόλητημνήμη πουδεσμεύουμεαυτόονομάζεταιδιαρροήμνήμης (memory leak) • Ηδιαρροήμνήμηςμπορείναοδηγήσεισεκαταστάσεις πουδενυπάρχειάλληελεύθερημνήμη • Προχωρημένο: όταντελειώνειηελεύθερημνήμητουυπολογιστή, τότετολειτουργικόστέλνεικάποιαμνήμηστονδίσκο, ελευθερώνειέτσιμνήμηκαιτηνξανα-χρησιμοποιεί. Ξαναφέρνειαπότονδίσκοστημνήμηότανγίνεται πρόσβασησεαυτήν. Αυτόοδηγείστοφαινόμενοtrashing όπουγράφεταικαιδιαβάζεταιαπότοδίσκοσυνέχειαμνήμη. Μεδιαρροήμνήμηςένα πρόγραμμαμπορείνααρχίσεινακάνειthrashing μετάαπόώρες, μέρες, ήχρόνιασυνεχήςλειτουργίαςτου. • Λύση:Αποδεσμεύουμεαμέσωςτημνήμη πουδενχρησιμοποιούμε (χρήσητηςfree)
Πρόβλημα 3: • ΧρήσηΜετάτηνΑποδέσμευση • Κάνουμεfree καιμετάξαναχρησιμοποιούμετηνίδιαμνήμη • Παρόμοιααποτελέσματαότανχρησιμοποιούμεμηδεσμευμένημνήμη • Αλλάζουμεμεταβλητές πουχρησιμοποιούμεχωρίςνατοξέρουμε – δύσκολαανιχνεύεται • Πάμεναγράψουμεσεμνήμη πουδενμαςανήκει (segmentation fault)καιτο πρόγραμμαδιακόπτεταιαυτόματα
Πρόβλημα 4: • Αποδέσμευσημνήμης πουδενέχουμεδεσμεύσει • Μπορείναμπερδέψειτολειτουργικόστηνδιαχείρισηελεύθερηςμνήμης
ΗΥ-150Προγραμματισμός ΔυναμικήΔιαχείρισηΜνήμης (2/2)
Δείκτεςσεδείκτες • char ch; char *pch; char **ppch;char ***ppch; • Δείκτηςσε πίνακααπόδείκτες (άγνωστοστηναρχήκαιτομέγεθοςτου πίνακακαιτομέγεθοςτων περιοχών πουδείχνουνοιδείκτες)
Δείκτεςσεδείκτες A[0][0] A[0][1] A[0][2] … A[0] A[1][0] ... A[1] A[2][0] ... A[i][j] A[2] A[3][0] ... A[3] A[4] A[4][0] ...A[4][9] #include <stdio.h> #include <stdlib.h> int main(void) { int lines = 5; /* Both lines and cols could be evaluated */ int cols = 10; /* or read in at run time */ int i, **Α; A = (int **)malloc(lines * sizeof(int *)); if (A == NULL) { puts("\nFailure to allocate room for row pointers.\n"); exit(0); } for (i = 0; i < lines; i++) { Α[i] = (int *) malloc(cols * sizeof(int)); if (Α[i] == NULL) { printf("\nFailure to allocate for row[%d]\n",i); exit(0); } printf("\n%d %p %d", i, Α[i], Α[i]); if (i > 0) printf(" %d",(int)(Α[i] - Α[i-1])); } //free the memory for (i = 0; i < lines; i++) free (Α[i]); free(A); return 0; } Εφαρμογή: πίνακας - εικόνα A
A = (int **)malloc(lines * sizeof(int *)); • if (A == NULL) • { • puts("\nFailure to allocate room for row pointers.\n"); • exit(0); • } • for (i = 0; i < lines; i++) • { • Α[i] = (int *) malloc(cols * sizeof(int)); • if (Α[i] == NULL) • { • printf("\nFailure to allocate for row[%d]\n",i); • exit(0); • } • printf("\n%d %p %d", i, Α[i], Α[i]); • if (i > 0) printf(" %d",(int)(Α[i] - Α[i-1])); • }
//free the memory • for (i = 0; i < lines; i++) • free (Α[i]); • free(A);
Συνοπτικά … • Έχουμε: int x; int *px; px = &x • Καιέναακόμαβήμα: int **ppx; ppx = &px; • Οπότεμπορούμενακάνουμε: **ppx = 42; /* Puts 42 into x */ • Αλλάόχι: *ppx = 42; /* ERROR!! */
Visually ADDR CONT SYMB int x; int *px; int **ppx; px = &x ppx = &px; **ppx = 42; 1000 42 x 2000 1000 px 3000 2000 ppx
ΠΡΟΣΟΧΗ! • ΔείκτεςσεΔείκτεςέναντιΔυσδιάστατουςΠίνακες • int a[10][20]; • a[r][c] είναισυντακτικάαποδεκτό • Δέσμευσημνήμης: 10×20 ακέραιοι • Υπολογισμόςδιεύθυνσηςτουa[r][c]: r*20+c+αρχήτου πίνακα • int **b; • b[r][c] είναισυντακτικάαποδεκτό • Δέσμευσημνήμηςγιαb:10 δείκτες b = (int **)malloc (10*sizeof(int*)) • Δέσμευσημνήμηςγιαb[i]: 20 ακέραιοισεκάθεγραμμή for (i = 0; i < 10; i++) b[i] = (int *) malloc(20 * sizeof(int)); • Υπολογισμόςδιεύθυνσηςτουb[r][c]: διεύθυνσημνήμηςτουδείκτηb[r] + c • ΠΡΙΝΤΟΝΥΠΟΛΟΓΙΣΜΟΤΟΥb[r][c] πρέπειναέχειγίνεισωστήανάθεσητιμώνσταb[r]καιδέσμευσημνήμης.
Strings • Χρήσηδεικτώνγιατηνδιαχείριση strings • Όπωςέχουμε πει: int a[ ] = {0, 1, 3, 5} • Καιηδιάστασηθακαθοριστείσε a[4] • Επίσης: char b[ ] = {‘t’, ‘e’, ‘s’, ‘t’, ‘\0’} char c[ ] = “test”;
Όμως … char aLine[ ] = “Hi, there!”; char *pLine = “Hi, there!”; • Παρόμοιααλλάόχιταίδια!!! • Το πρώτοκαθορίζειέναν πίνακαχαρακτήρων (11) πουμπορείνααλλαχθεί: aLine[4] = ‘T’; /* Σωστό! */ • Τοδεύτεροκαθορίζειένανδείκτη προςμίασταθεράτύπουstring πουδενμπορείνααλλαχθεί: pLine[4] = ‘T’; /* ΛΑΘΟΣ!! */ • Ενώαν: pLine = aLine; pLine[4] = ‘T’; /* Σωστό! */
Κιάλλα … • Όχιμόνοδείκτεςσεδείκτεςαλλάκαι πίνακεςαπόδείκτες! • char *name[ ] = {“Illegal”, “Jan”, ”Feb”, ”Mar”, ”Apr”, ”May”, “Jun”, “Jul”, “Aug”, “Sep”, “Oct”, “Nov”, “Dec”}; • Π.χ. name[2] είναιέναςδείκτηςστο character string ‘F’, ‘e’, ‘b’, ‘\0’ • Γιατίθαθέλαμεέναν πίνακααπόδείκτες;
Φανταστείτε : • Έχουμεονόματαόπως: • Smith, David • Leahy, Bill • Dagon, David • Humperdinck, Englebert • Υποθέστε πωςθέλουμεναταταξινομήσουμεαλφαβητικάαλλά παρατηρούμε πωςείναιδιαφορετικούμεγέθους. • Αντίναχρησιμοποιήσουμεέναναλγόριθμοταξινόμησης πουαλλάζειcharacter strings around γιατίναμηνχρησιμοποιήσουμεέναν πίνακααπόδείκτεςκαινααλλάζουμεδείκτες; • πολύ πιογρήγορο
Έχουμεέναν πίνακααπό pointers name[0] Smith, David name[1] Leahy, Bill name[2] Dagon, David name[3] Humperdinck, Englebert
Ταξινόμησημεμετακίνησηδεικτών name[0] Smith, David name[1] Leahy, Bill name[2] Dagon, David name[3] Humperdinck, Englebert