Table of Contents

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

Κατανόηση των διαφορών πλατφόρμας

Πριν από την εφαρμογή των τεχνικών φορητότητας, οι προγραμματιστές πρέπει να αναγνωρίσουν τα είδη των διαφορών που υπάρχουν μεταξύ των πλατφορμών. Αυτές οι διαφορές καλύπτουν τέσσερις ευρείες κατηγορίες: συμπεριφορά μεταγλωττιστή, λειτουργικά ⁇ συστήματα APIs, αρχιτεκτονική υλικού, και περιορισμούς πόρων.

Μεταβολές μεταγλωττιστών

Οι μεταγλωττιστές C ⁇ από το GCC, το Clang και το MSVC σε ενσωματωμένα εργαλεία όπως το IAR και το Keil ⁇ εφαρμόζουν το πρότυπο C με ποικίλα επίπεδα συμμόρφωσης. Μπορεί να διαφέρουν στον χειρισμό τους υπογραφής, διάταξης bit ⁇ field, διαμόρφωσης δομής και ακρίβειας σημασιολογίας ή . Οι επεκτάσεις γλωσσών (π.χ., επεκτάσεις GNU C, Microsoft ) μπορούν επίσης να δημιουργήσουν κρυμμένες εξαρτήσεις.

Διαφορές λειτουργικού συστήματος

Τα συστήματα τύπου POSIX (Linux, macOS, BSD) μοιράζονται πολλά APIs, αλλά τα Windows εκθέτουν ένα θεμελιωδώς διαφορετικό σύνολο κλήσεων συστήματος. File I/O, threading, δυναμική σύνδεση, σήματα και έλεγχος διαδικασιών συχνά απαιτούν είτε υπό όρους συλλογή ή ένα στρώμα αφαίρεσης. Ακόμα και η ευαισθησία περίπτωση όνομα και διαχωριστές διαδρομής (backslash vs. προς τα εμπρός slash) απαιτούν φροντίδα.

Αρχιτεκτονική υλικού και Ενδικότητα

Οι επεξεργαστές διαφέρουν στο μέγεθος λέξης (32 ⁇ bit vs. 64 ⁇ bit), στην τάξη byte (big ⁇ endian ή little ⁇ endian), στις απαιτήσεις ευθυγράμμισης και στα χαρακτηριστικά συνόλου εντολών. Ο κώδικας που υποθέτει είναι 32 bits ή ότι ένας δείκτης ταιριάζει σε μια θα αποτύχει σε πολλές πλατφόρμες. Η εντιμότητα γίνεται κρίσιμη όταν σειριάζει τα δεδομένα για τη μεταφορά ή την αποθήκευση αρχείων δικτύου.

Περιορισμοί πόρων

Ενσωματωμένα συστήματα ή βαθιά ενσωματωμένοι στόχοι ενδέχεται να μην διαθέτουν λειτουργικό σύστημα, να έχουν περιορισμένα μεγέθη στοίβας/βαρπών και να παρέχουν [[[LFT:6]] υλοποιήσεις με ειδικούς περιορισμένου σχήματος.

Βασικές βέλτιστες πρακτικές για φορητό κώδικα Γ

Βασιστείτε στις τυποποιημένες βιβλιοθήκες C

Η C τυποποιημένη βιβλιοθήκη (ISO/IEC 9899) παρέχει μια βασική γραμμή που κάθε συμμορφούμενος μεταγλωττιστής πρέπει να παρέχει. Λειτουργίες όπως , , και συμπεριφέρονται πανομοιότυπα σε όλες τις πλατφόρμες. Αποφύγετε τα αντίστοιχα της πλατφόρμας όπως (POSIX) εκτός αν φυλάσσεται από [. Για μαθηματικές πράξεις, προτιμάτε [ από τις βιβλιοθήκες-ειδικοί φορείς.

Χρήση τύπων σταθερού ⁇ ακεραίου πλάτους

Η κεφαλίδα ορίζει τύπους όπως , ] και που εγγυώνται ακριβή μεγέθη. Πάντα τα χρησιμοποιούν όταν το φάσμα των τιμών έχει σημασία ⁇ για παράδειγμα, όταν καθορίζουν τα ρυθμιστικά πρωτόκολλα ή τα μητρώα υλικού. Ομοίως, χρησιμοποιούν [ τους προσδιοριστές μορφής (], ) για να εκτυπώνουν αυτούς τους τύπους με φορητό τρόπο.

#include <stdint.h>
#include <inttypes.h>
int32_t val = -100;
printf("Value: %" PRId32 "\n", val);

Αποφύγετε τις Παραδοχές για Θεμελιώδεις Τύπους

Ποτέ μην υποθέτετε ότι είναι 32 bits, είναι 64 bits, ή ότι υπογράφεται. Χρησιμοποιήστε [ και σταθερές (]], ]) για να αντλήσετε ιδιότητες κατά τη στιγμή της μεταγλωττίσεως. Για δείκτες, χρησιμοποιήστε [ ή εάν πρέπει να τα αποθηκεύσετε ως ακέραια.

Χειριστείτε την Ενδικότητα Εύστοχα

Κατά την ανταλλαγή δυαδικών δεδομένων μεταξύ μηχανών (δικτύο, αρχείο ή κοινή μνήμη), πάντα μετατρέπονται σε γνωστή σειρά byte ⁇ συμβατικά εντολή byte δικτύου (μεγάλη ⁇ endian). Οι λειτουργίες POSIX , , ], είναι ευρέως διαθέσιμες· για μη-POSIX συστήματα, παρέχουν τις δικές σας υλοποιήσεις χρησιμοποιώντας [[LFT:35]]] και ανίχνευση χρόνου εκτέλεσης.

Αφηρημένη λειτουργία συστήματος αρχείων

Οι οριοθετητές διαδρομής αρχείων διαφέρουν ([[LFT:36]] στο Unix, [[LPT:37]] στα Windows). Χρησιμοποιήστε μακροεντολές ή μια μικρή λειτουργία χρησιμότητας που ομαλοποιεί τις διαδρομές. Για επανάληψη καταλόγου, το POSIX [[LFT:38]] API είναι στάνταρ· στα Windows μπορείτε να τυλίξετε [[LFT:39] πίσω από την ίδια διασύνδεση. Αποφύγετε τις σκληρές-κωδικοποιώντας απόλυτες διαδρομές.

Ελαχιστοποίηση Απροσδιόριστη και Εφαρμογή ⁇ Καθορισμένη Συμπεριφορά

Το πρότυπο C ορίζει πολλές λειτουργίες ως απροσδιόριστες ή υλοποιούμενες. Παραδείγματα περιλαμβάνουν την υπογεγραμμένη ακέραια υπερχείλιση, μετατόπιση κατά περισσότερο από το πλάτος του τύπου, και αξιολόγηση [. Χρησιμοποιήστε στατικούς αναλυτές όπως Cppcheck[] ή Clang ⁇ Tidy[] για να πιάσετε τέτοια μοτίβα, και να γράψετε κώδικα που είναι αυστηρά σύμφωνος.

Μακροεντολές προεπεξεργαστών για τη Σύνταξη ⁇ Επιλογή χρόνου

Η συλλογή υπό όρους είναι απαραίτητη για τον συγκεκριμένο κώδικα πλατφόρμας, αλλά η κατάχρηση μπορεί να δημιουργήσει ένα μπερδεμένο χάος. Χρησιμοποιήστε γνωστές προκαθορισμένες μακροεντολές: , , , ], και μακροεντολές μεταγλωττιστών όπως . Πάντα τεκμηριώνουν το καθένα και διατηρούν τα τμήματα πλατφόρμας ⁇ συγκεκριμένα μικρά.

#ifdef _WIN32
 #include <windows.h>
 #define SLEEP(ms) Sleep(ms)
#else
 #include <unistd.h>
 #define SLEEP(ms) usleep((ms)*1000)
#endif

Χρήση των Layers Αφηρημένης Θέσης για κλήσεις συστήματος

Για την κοχλία, τις υποδοχές, τους χρονοδιακόπτες και τη διαχείριση της μνήμης, δημιουργήστε λεπτά περιτυλίγματα. Για παράδειγμα, ορίστε έναν [[LFT:48]] τύπο και λειτουργία που χαρτογραφεί σε νήματα POSIX στο Unix και στα Windows. Η ίδια προσέγγιση λειτουργεί για δυναμικές βιβλιοθήκες ( vs. ). Πολλές βιβλιοθήκες ανοικτού κώδικα (π.χ., plibc[[LFT:1]], Apache APR]) παρέχουν ήδη τέτοιες αφηρημένες πληροφορίες.

Δοκιμή σε πολλαπλές πλατφόρμες νωρίς και συχνά

Οι αγωγοί συνεχούς ολοκλήρωσης (CI) θα πρέπει να μεταγλωττίζουν και να εκτελούν τη σουίτα δοκιμών σε Linux, macOS, Windows, και κάθε ενσωματωμένο στόχο. Χρησιμοποιήστε cross-compilers και εξομοιωτές (π.χ., QEMU) για να πιάσει αρχιτεκτονική ⁇ ειδικά σφάλματα πριν από την ανάπτυξη. Αυτοματοποιημένη δοκιμή με εργαλεία όπως []ctest[ ή CMake/CTest βοηθά στην επιβολή της φορητότητας.

Προηγμένες τεχνικές φορητότητας

Κατασκευή ρυθμίσεων συστήματος με CMake ή Autotools

Τα σύγχρονα συστήματα κατασκευής μπορούν να ανιχνεύσουν χαρακτηριστικά πλατφόρμας κατά τη ρύθμιση του χρόνου. CMake’s [[LFT:53]], [[LFT:54]]], και [[LFT:55]] οι ενότητες παράγουν ένα [[LFT:56]] που ο κωδικός σας μπορεί να περιλαμβάνει. Αυτό αντικαθιστά εύθραυστες [[LFT:57] αλυσίδες με ένα μόνο σημείο αλήθειας.

// Generated config.h
#define HAVE_STDINT_H 1
#define WORDS_BIGENDIAN 0
#define SIZEOF_LONG 8

Φορητό εσωτερικό συγκρότημα και Intrinsics

Όταν η απόδοση απαιτεί την πλατφόρμα ⁇ ειδικές οδηγίες (π.χ., SIMD, CPUID), τα περικλείει σε ξεχωριστά αρχεία και επιλέγει το σωστό αρχείο κατά τη διάρκεια της κατασκευής. Χρησιμοποιήστε μεταγλωττιστή εγγενείς (όπως [[LFT:59]] από GCC/Clang/ICC/VS) και όχι την εσωτερική συναρμολόγηση, επειδή οι εγγενείς είναι πιο φορητές σε όλους τους μεταγλωττιστές στην ίδια αρχιτεκτονική.

Στοίχιση δομών δεδομένων

Για τους παλαιότερους μεταγλωττιστές, οι προεπεξεργαστές χρησιμοποιούν τις εργασίες που βασίζονται σε C11 ([[LFT:62]]]) για την GCC, [[LFT:64] για την MSVC.

Συμβατότητα χειρισμού σήματος

Οι σταθερές σημάτων (, ]) και ο ασφαλής χειρισμός σήματος διαφέρουν ευρέως. Το POSIX API είναι προτιμότερο από το παλαιότερο . Στα Windows, τα σήματα μιμούνται μέσω των χειριστών ελέγχου κονσόλας.

Πραγματικές ⁇ Παγκόσμιες Παγίδες και Πώς να τους Αποφύγετε

Βασιζόμενοι σε Χωρίς Αναστροφή

είναι στάνταρ για το POSIX αλλά λείπει σε πολλές ενσωματωμένες πλατφόρμες και παλαιότερα περιβάλλοντα Windows. Χρησιμοποιήστε μια φορητή εφαρμογή όπως [[LPT:0]]plibc[[LFT:1]] ή δέστε ένα ελάχιστο [[LFT:71]] κάτω από μια ανεκτική άδεια.

Υποθέτοντας είναι ένας Υπογραφόμενος Ακέραιος

Το πρότυπο C λέει μόνο [[LFT:73]] είναι ένας πραγματικός τύπος που μπορεί να αναπαραστήσει τους χρόνους. Σε ορισμένα ενσωματωμένα συστήματα είναι μια ανυπόγραφη τιμή 32 ⁇ bit· σε άλλα είναι ένας 64 ⁇ bit υπογεγραμμένος ακέραιος. Ποτέ μην εκτελείς αριθμητική στο [[LFT:74]] χωρίς να ελέγχεις τις ιδιότητές του, ή να χρησιμοποιείς [[LFT:75]] για διαφορές.

Παράβλεψη νημάτων ⁇ Ασφάλεια στις κλήσεις συστήματος

Λειτουργίες όπως , και [[LFT:78]] χρησιμοποιούν στατικά ρυθμιστικά μέσα και δεν είναι σπείρωμα ⁇ ασφαλείς. Χρησιμοποιήστε τις παραλλαγές του επαναπληρωτή (], [[LFT:80]]]) όπου είναι δυνατόν, και παρέχουν εφεδρικές υλοποιήσεις σε πλατφόρμες που δεν τις διαθέτουν.

Συμπέρασμα

Η συγγραφή φορητού κώδικα C είναι μια πειθαρχία και μια επένδυση. Με την προσκόλληση στο πρότυπο C, επιλέγοντας σταθερούς-πλάτους τύπους, αφηρημένες διεπαφές συστημάτων, και τη δοκιμή σε πολλαπλές πλατφόρμες, οι προγραμματιστές μπορούν να παράγουν λογισμικό που τρέχει αξιόπιστα σε περιβάλλοντα που κυμαίνονται από υπερυπολογιστές σε μικροελεγκτές. Οι πρακτικές που περιγράφονται εδώ ⁇ σε συνδυασμό με τη σύγχρονη διαμόρφωση συστήματος κατασκευής και στατικής ανάλυσης ⁇ αποτελούν ένα ανθεκτικό θεμέλιο για την ανάπτυξη της διατομής C. Θυμηθείτε: η φορητότητα δεν είναι μια μεταμελετημένη σκέψη· είναι ένας στόχος σχεδιασμού που πληρώνει μερίσματα σε όλη τη διάρκεια του κύκλου ζωής ενός έργου.