Table of Contents
Kannettavien C-koodien kirjoittaminen on ammatillisen ohjelmistosuunnittelun kulmakivi, jonka avulla sovellukset voivat kulkea eri laitteistoarkkitehtuurien, käyttöjärjestelmien ja kääntäjien välillä mahdollisimman pienellä työllä. Portability vähentää huoltoa, laajentaa käyttäjäkantaa ja tulevaisuudensuojaa kehittyviä alustoja vastaan. Tämä artikkeli poistaa taistelutestatut parhaat käytännöt todellisen siirrettävyyden saavuttamiseksi, joka perustuu C-standardiin ja vuosikymmenien reaalimaailman kokemukseen.
Platform-erojen ymmärtäminen
Ennen kuin kehittäjät soveltavat siirrettävyyttä koskevia tekniikoita, niiden on tunnustettava, millaisia eroja alustojen välillä on. Erot ulottuvat neljään laajaan luokkaan: kääntäjän käyttäytymiseen, käyttöjärjestelmän sovellusrajapintoihin, laitteistoarkkitehtuuriin ja resurssirajoitteisiin.
Muokkaa muutoksia
C-kääntäjät . GCC:stä, Clang:sta ja MSVC:stä sulautettuihin työkaluihin, kuten IAR:iin ja Keil ... toteuttaa C-standardin vaihtelevalla tavalla. Ne voivat vaihdella [-allekirjoitus-, bittikenttäasettelu, rakennepehmusteet, ja tarkka semantiikka [ tai . Kielilaajennukset (esim., GNU C laajennukset, Microsoft.) voivat myös luoda piilotettuja riippuvuuksia.
Käyttöjärjestelmän erot
POSIX-tyyppiset järjestelmät (Linux, macOS, BSD) jakavat monia sovellusrajapintoja, mutta Windows paljastaa aivan eri järjestelmäpuhelut. Tiedosto I/O, kiertyminen, dynaaminen linkittäminen, signaalit, ja prosessinohjaus vaativat usein joko ehdollisen koosteen tai abstraktio kerros. Jopa tiedostonimelle kotelon herkkyys ja polun erottimet (backslash vs. etuviiva) vaativat varovaisuutta.
Laitteistoarkkitehtuuri ja endianness
Prosessorit eroavat toisistaan sanakoossa (32-bittinen vs. 64-bittinen), tavujärjestyksessä (big-endiaani tai pikku-endiaani), linjausvaatimuksissa ja ohjeissa. Koodi, joka olettaa [ on 32 bittiä tai että osoitin sopii []:een, epäonnistuu monilla alustoilla. Endianness muuttuu kriittiseksi, kun tieto sarjoitetaan verkon siirtoon tai tiedostojen tallentamiseen.
Resurssirajoitteet
Upotetut järjestelmät tai syvälle upotetut kohteet voivat olla käyttöjärjestelmän puutteessa, niillä on rajoitettu pino/kooltaan ja ne tarjoavat [ toteutuksia rajoitetulla muotomäärityksellä. Kannettavassa koodissa on vältettävä oletuksia muistin saatavuudesta ja ajoajasta.
Kannettavaa C-koodia koskevat keskeiset parhaat käytännöt
Rely on Standard C Kirjastot
C-standardikirjasto (ISO/IEC 9899) tarjoaa perustason, jota jokaisen vastaavan kääntäjän on toimitettava. Toiminnot, kuten [, , [] ja , ovat samanlaisia eri alustoilla. Vältä alustakohtaisia vastaavia ominaisuuksia, kuten [ (POSIX) ellei niitä ole vartioitu . Matematiikan osalta suositaan myyjäkohtaisia vektorikirjastoja.
Käytä kiinteitä ja väljähtyviä tyyppejä
-otsikko määrittelee tyypit kuten , ja , jotka takaavat tarkat koot. Käytä niitä aina, kun arvovalikoimalla on merkitystä . Esimerkiksi protokollapuskurien tai laiterekisterien määrittelyssä. Samoin käytä -muotoa specifiereitä (], ]) näiden tyyppien tulostamiseen portaaallisesti.
#include <stdint.h>
#include <inttypes.h>
int32_t val = -100;
printf("Value: %" PRId32 "\n", val);
Vältä oletuksia perustyypeistä
Älä koskaan oleta, että on 32 bittiä, on 64 bittiä tai että on allekirjoitettu. Käytä ja vakioita ([], []) johtaa ominaisuuksia kääntöhetkellä. Osoittimia varten käytetään tai , jos ne on säilytettävä kokonaislukuina.
Käsinhoitokyky
Kun binääritietoja vaihdetaan koneiden välillä (verkko, tiedosto tai yhteinen muisti), ne muuntuvat aina tunnetuksi tavutilaukseksi . Perinteisesti verkkotavutilaukseksi (big-endian). POSIX-toiminnot , , , [[]], ovat yleisesti saatavilla; ei-POSIX-järjestelmille, tarjoavat omat toteutustesi ja ajoajan toteamisen avulla.
Abstrakti tiedostojärjestelmän toiminta
Tiedostopolun erottimet eroavat toisistaan ([ on Unix, Windowsissa). Käytä makroja tai pientä aputoimintoa, joka normalisoi polkuja. Hakemistoiterointia varten POSIX API on vakio; Windowsissa voit kääriä saman käyttöliittymän taakse. Vältä kovakoodaamista absoluuttisia polkuja.
Minimoi määrittelemätön ja täytäntöönpanoon perustuva käyttäytyminen
C-standardissa monet toiminnot määritellään määrittelemättömäksi tai täytäntöönpanoksi. Esimerkkejä ovat allekirjoitettu kokonaislukuylivuoto, siirtyminen enemmän kuin tyypin leveys, ja arviointi [. Käytä staattisia analysaattorit kuten Cppcheck tai ]Clang-Tidy[] saalistaa tällaisia kuvioita ja kirjoita koodi, joka on tiukasti vaatimusten mukainen.
Vivutus esiprosessori Makrot valinta-aika valinta
Ehdollinen kooste on välttämätön alustakohtaisen koodin kannalta, mutta väärinkäyttö voi aiheuttaa sotkun. Käytä tunnettuja ennalta määriteltyjä makroja: , , , , [, ja kääntäjä makrot kuten [. Aina dokumentoi jokainen haara ja pidä alustakohtaiset osiot pieninä.
#ifdef _WIN32
#include <windows.h>
#define SLEEP(ms) Sleep(ms)
#else
#include <unistd.h>
#define SLEEP(ms) usleep((ms)*1000)
#endif
Käytä järjestelmäpuhelujen abstraktia tasoa
Esimerkiksi määrittele tyyppi ja toiminto, joka kartoittaa POSIX-langoille Unix- ja Windows-langoilla. Sama lähestymistapa toimii dynaamisille kirjastoille ([] vs. ). Monet avoimen lähdekoodin kirjastot (esim. ]plibc[]], [[]]Apache APR[]) tarjoavat jo tällaisia abstraktioita.
Useiden alustojen testaus varhaisessa vaiheessa ja usein
Jatkuva integrointi (CI) putkistojen tulisi koota ja ajaa testisarja Linuxissa, MacOSissa, Windowsissa ja missä tahansa upotetussa kohteessa. Käytä ristikomplikaattoreita ja emulaattoreita (esim. QEMU) arkkitehtuurin erityisten vikojen kiinni saamiseksi ennen käyttöönottoa. Automaattinen testaus esimerkiksi [ctest[] tai CMake/CTest avulla voidaan valvoa siirrettävyyttä.
Edistyneet siirrettävyystekniikat
Koosta järjestelmäasetukset CMAke- tai Autotools-työkaluilla
Nykyaikaiset rakennusjärjestelmät voivat havaita alustan ominaisuudet konfiguraatio-aikana. CMAke.s , [ ja moduulit tuottavat [] että koodi voi sisältää. Tämä korvaa hauraat [ ketjut yhdellä totuuden pisteellä.
// Generated config.h
#define HAVE_STDINT_H 1
#define WORDS_BIGENDIAN 0
#define SIZEOF_LONG 8
Kannettavat inline-kokoonpanot ja sisärenkaat
Kun suorituskyky vaatii alustakohtaisia ohjeita (esim. Simd, CPUID), tiivistä ne erillisiin tiedostoihin ja valitse oikea tiedosto rakentamisen aikana. Käytä kääntäjän luomuksia (kuten GCC/Clang/ICC/VS:stä) eikä inline-kokoonpanoa, koska luontaisuudet ovat kannettavampia samassa arkkitehtuurissa.
Tasaa datarakenteet nimenomaisesti
Rakennepakkaus ja linjaus vaihtelevat. Käytä ja -erikoisnumeroita C11 ():sta linjauksen toteuttamiseen. Vanhempien kääntäjien osalta käytä esiprosessoripohjaisia työpiirroksia () GCC:ssä, [ MSVC:ssä.
Signaalinkäsittelyn yhteensopivuus
Signaalivakiot (], ) ja turvallinen signaalinkäsittely vaihtelevat suuresti. POSIX API on parempi kuin vanhempi . Windowsissa signaalit emuloidaan konsolin ohjaimien kautta.
Todelliset vitfalls ja miten välttää niitä
Lentäminen Ilman takaiskua
on POSIX-standardi, mutta puuttuu monilta upotetuilla alustoilla ja vanhoissa Windows-ympäristöissä. Käytä kannettavaa toteutusta kuten plibc[] tai niputa minimaalinen [] luvan nojalla.
Olettaen on allekirjoitettu Integer
C-standardissa sanotaan vain , että se on todellinen tyyppi, joka pystyy edustamaan aikoja. Joissakin sulautetuissa järjestelmissä se on allekirjoitusta vailla oleva 32-bittinen arvo; toisissa se on 64-bittinen allekirjoitettu kokonaisluku. Älä koskaan tee aritmeettista :lla ilman, että tarkistat sen ominaisuuksia, tai käytä [:llä eroihin.
Säieturvallisuuden poistaminen järjestelmäpuheluissa
, ja käyttävät staattisia puskureita eivätkä ole langansuojattuja. Käytä reentrantin muunnelmia (], []) aina kun se on mahdollista ja anna varatoteutuksia alustoilla, joilta niitä puuttuu.
Päätelmät
Kirjoittamalla kannettava C-koodi on sekä kurinpidollinen että investointi. Pysymällä tiiviisti C-standardiin, valitsemalla kiinteän leveyden tyyppejä, abstrakti järjestelmärajapintoja ja testaamalla useita alustoja, kehittäjät voivat tuottaa ohjelmistoja, jotka toimivat luotettavasti ympäristöissä, jotka vaihtelevat supertietokoneista mikroohjaimiin. Tässä kuvatut käytännöt yhdistettynä modernin rakennusjärjestelmän konfiguraatioon ja staattiseen analyysiin . Muista: siirrettävyys ei ole jälkiajattelua; se on suunnittelutavoite, joka maksaa osinkoja koko projektin elinkaaren ajan.