Πώς να κατανοήσετε τη μνήμη του προγράμματος σας

Όταν κωδικοποιείτε σε γλώσσα όπως C ή C ++, μπορείτε να αλληλεπιδράσετε με τη μνήμη σας με πιο χαμηλό επίπεδο. Μερικές φορές αυτό δημιουργεί πολλά προβλήματα που δεν είχατε πριν: segfaults . Αυτά τα σφάλματα είναι αρκετά ενοχλητικά και μπορεί να σας προκαλέσουν πολλά προβλήματα. Είναι συχνά δείκτες ότι χρησιμοποιείτε μνήμη που δεν πρέπει να χρησιμοποιείτε.

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

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

Πώς διαιρείται η μνήμη;

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

Η μνήμη στοίβας έχει ένα σύνολο τρόπων και λειτουργιών για την εργασία της. Εκεί αποθηκεύονται ορισμένες από τις πληροφορίες μητρώου του επεξεργαστή σας. Και είναι εκεί που πηγαίνουν σχετικές πληροφορίες σχετικά με το πρόγραμμά σας - ποιες συναρτήσεις ονομάζονται, ποιες μεταβλητές δημιουργήσατε και μερικές περισσότερες πληροφορίες. Αυτή η μνήμη διαχειρίζεται επίσης το πρόγραμμα και όχι ο προγραμματιστής.

Ο σωρός χρησιμοποιείται συχνά για να εκχωρήσει μεγάλες ποσότητες μνήμης που υποτίθεται ότι υπάρχουν όσο το επιθυμεί ο προγραμματιστής. Ωστόσο, είναι έργο του προγραμματιστή να ελέγχει τη χρήση της μνήμης στο σωρό . Όταν δημιουργείτε σύνθετα προγράμματα, συχνά πρέπει να διαθέσετε μεγάλα κομμάτια μνήμης και εκεί είναι που χρησιμοποιείτε το σωρό. Το ονομάζουμε δυναμική μνήμη .

Βάζετε πράγματα στο σωρό κάθε φορά που χρησιμοποιείτε mallocγια να διαθέσετε μνήμη για κάτι. Οποιαδήποτε άλλη κλήση μοιάζει με int i;μνήμη στοίβας. Γνωρίζοντας αυτό είναι πραγματικά σημαντικό, ώστε να μπορείτε να βρείτε εύκολα σφάλματα στο πρόγραμμά σας και να βελτιώσετε περαιτέρω την αναζήτηση σφάλματος Segfault.

Κατανόηση της στοίβας

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

Λοιπόν, πώς λειτουργεί πραγματικά;

Η στοίβα είναι μια δομή δεδομένων LIFO (Last-In-First-Out). Μπορείτε να το δείτε ως ένα κουτί από τέλεια προσαρμοσμένα βιβλία - το τελευταίο βιβλίο που τοποθετείτε είναι το πρώτο που βγάζετε. Χρησιμοποιώντας αυτήν τη δομή, το πρόγραμμα μπορεί εύκολα να διαχειριστεί όλες τις λειτουργίες και τα πεδία του χρησιμοποιώντας δύο απλές λειτουργίες: push και pop .

Αυτά τα δύο κάνουν ακριβώς το αντίθετο μεταξύ τους. Το Push εισάγει την τιμή στην κορυφή της στοίβας. Το Pop παίρνει την κορυφαία τιμή από αυτό.

Για να παρακολουθείτε την τρέχουσα θέση μνήμης, υπάρχει ένας ειδικός καταχωρητής επεξεργαστών που ονομάζεται Stack Pointer . Κάθε φορά που πρέπει να αποθηκεύσετε κάτι - όπως μια μεταβλητή ή τη διεύθυνση επιστροφής από μια συνάρτηση - ωθεί και μετακινεί το δείκτη στοίβας προς τα πάνω. Κάθε φορά που βγείτε από μια συνάρτηση, εμφανίζει τα πάντα από το δείκτη στοίβας έως την αποθηκευμένη διεύθυνση επιστροφής από τη συνάρτηση. Είναι απλό!

Για να ελέγξετε αν καταλάβατε, ας χρησιμοποιήσουμε το ακόλουθο παράδειγμα (δοκιμάστε να βρείτε μόνο το σφάλμα ☺️):

Εάν το εκτελέσετε, το πρόγραμμα θα είναι απλώς προεπιλεγμένο. Γιατί συμβαίνει αυτό; Όλα φαίνονται στη θέση τους! Εκτός από… τη στοίβα.

Όταν καλούμε τη συνάρτηση createArray, η στοίβα:

  • αποθηκεύει τη διεύθυνση επιστροφής,
  • δημιουργεί arrστη μνήμη στοίβας και την επιστρέφει (ένας πίνακας είναι απλά ένας δείκτης σε μια θέση μνήμης με τις πληροφορίες του)
  • αλλά αφού δεν το χρησιμοποιήσαμε malloc, αποθηκεύεται στη μνήμη στοίβας.

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

Κατανόηση του σωρού

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

Το Malloc (κατανομή μνήμης) ζητά από το σύστημα το ποσό της μνήμης που ζητήθηκε και επιστρέφει ένα δείκτη στη διεύθυνση εκκίνησης. Το Free λέει στο σύστημα ότι η μνήμη που ζητήσαμε δεν χρειάζεται πλέον και μπορεί να χρησιμοποιηθεί για άλλες εργασίες. Φαίνεται πολύ απλό - εφ 'όσον αποφεύγετε λάθη.

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

Το Memory Leak είναι η μνήμη που ζητήθηκε από τον χρήστη που δεν απελευθερώθηκε ποτέ - όταν το πρόγραμμα έληξε ή χάθηκαν δείκτες στις τοποθεσίες του. Αυτό κάνει το πρόγραμμα να χρησιμοποιεί πολύ περισσότερη μνήμη από ό, τι έπρεπε. Για να το αποφύγουμε αυτό, κάθε φορά που δεν χρειαζόμαστε πλέον ένα σωρευτικό στοιχείο, το ελευθερώνουμε

Στην παραπάνω εικόνα, ο κακός τρόπος δεν ελευθερώνει ποτέ τη μνήμη που χρησιμοποιήσαμε. Αυτό καταλήγει να χάνει 20 * 4 byte (μέγεθος int σε 64-bit) = 80 byte. Αυτό μπορεί να μην φαίνεται τόσο πολύ, αλλά φανταστείτε να μην το κάνετε σε ένα τεράστιο πρόγραμμα. Μπορούμε να καταλήξουμε να σπαταλάμε gigabyte!

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

Μπόνους: Δομές και σωρός

Ένα από τα συνηθισμένα λάθη κατά τη χρήση της δομής είναι να απελευθερώσετε την δομή. Αυτό είναι εντάξει, αρκεί να μην διαθέτουμε μνήμη σε δείκτες εντός της δομής. Εάν η μνήμη κατανέμεται σε δείκτες εντός της δομής, πρέπει πρώτα να τους ελευθερώσουμε. Τότε μπορούμε να απελευθερώσουμε ολόκληρη τη δομή

Πώς μπορώ να λύσω τα προβλήματα διαρροής μνήμης μου

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

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

(Εάν θέλετε να μάθετε περισσότερα σχετικά με την ευκολότερη ανάγνωση του κώδικα, ελέγξτε την ανάρτησή μου σχετικά με την αφαίρεση).

Ένα εξαιρετικό εργαλείο διαχείρισης μνήμης - Valgrind

Είναι δύσκολο να διαχειριστείτε τη μνήμη σας και να βεβαιωθείτε ότι έχετε χειριστεί τα πάντα σωστά. Ένα εξαιρετικό εργαλείο για επικύρωση εάν το πρόγραμμά σας συμπεριφέρεται σωστά είναι το Valgrind. Αυτό το εργαλείο επικυρώνει το πρόγραμμά σας, που σας λέει πόση μνήμη εκχωρήσατε, πόσο απελευθερώθηκε, εάν προσπαθήσατε να γράψετε σε εσφαλμένη περιοχή μνήμης… Η χρήση του είναι ένας πολύ καλός τρόπος για να επικυρώσετε εάν όλα είναι εντάξει και πρέπει να το χρησιμοποιήσετε για να αποφύγετε συμβιβασμούς ασφαλείας.

Μην ξεχάσετε να με ακολουθήσετε!

Εκτός από την ανάρτηση εδώ στο Medium, είμαι επίσης στο Twitter.

Εάν έχετε οποιεσδήποτε ερωτήσεις ή προτάσεις, μην διστάσετε να επικοινωνήσετε μαζί μου.