Προγραμματισμός

4 κοινά λάθη προγραμματισμού C - και 5 συμβουλές για την αποφυγή τους

Λίγες γλώσσες προγραμματισμού μπορούν να ταιριάξουν με το C για καθαρή ταχύτητα και ισχύ σε επίπεδο μηχανής. Αυτή η δήλωση ήταν αλήθεια πριν από 50 χρόνια και εξακολουθεί να ισχύει σήμερα. Ωστόσο, υπάρχει ένας λόγος που οι προγραμματιστές επινόησαν τον όρο "ποδόσφαιρο" για να περιγράψουν το είδος ισχύος του Γ. Εάν δεν είστε προσεκτικοί, το C μπορεί να εκτοξεύσει τα δάχτυλα των ποδιών σας - ή κάποιον άλλο.

Εδώ είναι τέσσερα από τα πιο συνηθισμένα λάθη που μπορείτε να κάνετε με το C και πέντε βήματα που μπορείτε να ακολουθήσετε για να τα αποτρέψετε.

Κοινό λάθος C: Χωρίς απελευθέρωση malloc-με μνήμη (ή την απελευθέρωση περισσότερες από μία φορές)

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

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

Κοινό λάθος C: Ανάγνωση ενός πίνακα εκτός ορίων

Εδώ έχουμε ένα ακόμη από τα πιο κοινά και επικίνδυνα λάθη στο C. Μια ανάγνωση μετά το τέλος ενός πίνακα μπορεί να επιστρέψει δεδομένα απορριμμάτων. Ένα γράψιμο πέρα ​​από τα όρια ενός πίνακα μπορεί να καταστρέψει την κατάσταση του προγράμματος ή να το καταστρέψει εντελώς ή, το χειρότερο από όλα, να γίνει φορέας επίθεσης για κακόβουλο λογισμικό.

Γιατί λοιπόν το βάρος του ελέγχου των ορίων ενός πίνακα εναπόκειται στον προγραμματιστή; Στην επίσημη προδιαγραφή C, η ανάγνωση ή η σύνταξη ενός πίνακα πέρα ​​από τα όριά της είναι «απροσδιόριστη συμπεριφορά», που σημαίνει ότι η προδιαγραφή δεν έχει λόγο για το τι υποτίθεται ότι θα συμβεί. Ο μεταγλωττιστής δεν χρειάζεται καν να παραπονεθεί.

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

Κοινό λάθος C: Δεν ελέγχονται τα αποτελέσματα του malloc

malloc και κάλκο (για προ-μηδενική μνήμη) είναι οι λειτουργίες της βιβλιοθήκης C που λαμβάνουν μνήμη εκχωρημένη από σωρό από το σύστημα. Εάν δεν είναι σε θέση να εκχωρήσουν μνήμη, δημιουργούν ένα σφάλμα. Πίσω στις μέρες που οι υπολογιστές είχαν σχετικά μικρή μνήμη, υπήρχε μια καλή ευκαιρία να καλέσετε malloc μπορεί να μην είναι επιτυχής.

Παρόλο που οι υπολογιστές σήμερα διαθέτουν gigabytes RAM για να τα πετάξουν, υπάρχει πάντα η πιθανότητα malloc μπορεί να αποτύχει, ειδικά υπό υψηλή πίεση μνήμης ή κατά την εκχώρηση μεγάλων πλακών μνήμης ταυτόχρονα. Αυτό ισχύει ιδιαίτερα για προγράμματα C που "κατανέμουν πλάκα" ένα μεγάλο μπλοκ μνήμης από το λειτουργικό σύστημα πρώτα και στη συνέχεια το διαιρούν για δική τους χρήση. Εάν αυτή η πρώτη κατανομή αποτύχει επειδή είναι πολύ μεγάλη, ενδέχεται να μπορείτε να παγιδεύσετε αυτήν την άρνηση, να μειώσετε την κατανομή και να συντονίσετε ανάλογα τη ευρετική χρήση της μνήμης του προγράμματος. Αλλά εάν η κατανομή της μνήμης αποτύχει ανεστραμμένη, ολόκληρο το πρόγραμμα θα μπορούσε να πάει κοιλιά.

Κοινό λάθος C: Χρήση κενός* για γενικούς δείκτες στη μνήμη

Χρησιμοποιώνταςκενός* Το να δείξεις στη μνήμη είναι μια παλιά συνήθεια - και μια κακή. Οι δείκτες στη μνήμη πρέπει πάντα να είναι απανθρακώνω*, χωρίς υπογραφή char *, ήuintptr_t *. Οι σύγχρονες σουίτες μεταγλωττιστών C πρέπει να παρέχουν uintptr_t ως μέρος του stdint.h

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

Αποφυγή κοινών λαθών C - 5 συμβουλές

Πώς αποφεύγετε αυτά τα πολύ συνηθισμένα λάθη όταν εργάζεστε με μνήμη, πίνακες και δείκτες στο C; Λάβετε υπόψη αυτές τις πέντε συμβουλές.

Δομή προγραμμάτων C, έτσι ώστε η ιδιοκτησία για τη μνήμη διατηρείται σαφής

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

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

Χρησιμοποιήστε επιλογές μεταγλωττιστή C που προστατεύουν από προβλήματα μνήμης

Πολλά από τα προβλήματα που περιγράφονται στο πρώτο μισό αυτού του άρθρου μπορούν να επισημανθούν χρησιμοποιώντας αυστηρές επιλογές μεταγλωττιστή. Πρόσφατες εκδόσεις του gcc, για παράδειγμα, να παράσχετε εργαλεία όπως το AddressSanitizer ("ASAN") ως επιλογή συλλογής για έλεγχο ενάντια σε κοινά λάθη διαχείρισης μνήμης.

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

Χρησιμοποιήστε το Cppcheck ή το Valgrind για να αναλύσετε τον κώδικα C για διαρροές μνήμης

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

Το Cppcheck εκτελεί στατική ανάλυση στον πηγαίο κώδικα C για να αναζητήσει κοινά λάθη στη διαχείριση μνήμης και σε απροσδιόριστες συμπεριφορές (μεταξύ άλλων).

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

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

Αυτοματοποιήστε τη διαχείριση μνήμης C με έναν συλλέκτη απορριμμάτων

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

Ναι, αυτό είναι δυνατό στο C. Μπορείτε να χρησιμοποιήσετε κάτι όπως το συλλέκτη απορριμμάτων Boehm-Demers-Weiser για να προσθέσετε αυτόματη διαχείριση μνήμης σε προγράμματα C. Για ορισμένα προγράμματα, η χρήση του συλλέκτη Boehm μπορεί ακόμη και να επιταχύνει τα πράγματα. Μπορεί ακόμη και να χρησιμοποιηθεί ως μηχανισμός ανίχνευσης διαρροών.

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

Μην χρησιμοποιείτε το C όταν θα κάνει άλλη γλώσσα

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

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

Μια άλλη προσέγγιση είναι να χρησιμοποιήσετε το C μόνο για την πραγματικά εντατική απόδοση ανταλλακτικά της εφαρμογής και μια πιο αξιόπιστη αν και πιο αργή γλώσσα για άλλα μέρη. Και πάλι, το Python μπορεί να χρησιμοποιηθεί για την αναδίπλωση βιβλιοθηκών C ή προσαρμοσμένου κώδικα C, καθιστώντας το μια καλή επιλογή για τα περισσότερα στοιχεία boilerplate όπως χειρισμός επιλογών γραμμής εντολών.

$config[zx-auto] not found$config[zx-overlay] not found