Programovanie

4 bežné chyby v programovaní v jazyku C - a 5 tipov, ako sa im vyhnúť

Len málo programovacích jazykov sa vyrovná C s úplnou rýchlosťou a výkonom na úrovni stroja. Toto tvrdenie bolo pravdivé pred 50 rokmi a platí dodnes. Existuje však dôvod, prečo programátori vytvorili termín „footgun“ na opísanie druhu sily C. Ak si nedáte pozor, môže vám C odfúknuť prsty na nohách - alebo niekoho iného.

Tu sú štyri z najbežnejších chýb, ktoré môžete v C urobiť, a päť krokov, ktoré môžete urobiť, aby ste im zabránili.

Bežná chyba C: neuvoľňovanie malloc-ed pamäť (alebo jej viacnásobné uvoľnenie)

Toto je jedna z veľkých chýb v jazyku C, z ktorých mnohé zahŕňajú správu pamäte. Pridelená pamäť (vykonáva sa pomocou malloc funkcia) sa automaticky nezlikviduje v C. Je úlohou programátora zlikvidovať túto pamäť, keď sa už nepoužíva. Ak sa vám nepodarí uvoľniť opakované požiadavky na pamäť, skončí sa únikom pamäte. Skúste použiť oblasť pamäte, ktorá je už uvoľnená, a váš program zlyhá - alebo, čo je horšie, ochabne a stane sa zraniteľným voči útoku pomocou tohto mechanizmu.

Pamätajte, že spomienka únik by mal popisovať iba situácie, kde je pamäť predpokladaný byť oslobodený, ale nie je. Ak program stále alokuje pamäť, pretože pamäť je skutočne potrebná a používa sa na prácu, potom môže byť jej využitie pamäťouneefektívne, ale prísne povedané, nejde o únik.

Bežná chyba C: Čítanie poľa mimo hraníc

Tu máme ešte jednu z najbežnejších a najnebezpečnejších chýb v jazyku C. Čítanie za koncom poľa môže vrátiť údaje o odpadkoch. Zápis za hranice poľa môže narušiť stav programu alebo ho úplne zrútiť alebo, čo je najhoršie, stať sa vektorom útoku škodlivého softvéru.

Prečo teda bremeno kontroly hraníc poľa zostáva na programátorovi? V oficiálnej špecifikácii C je čítanie alebo zápis poľa za jeho hranicami „nedefinované správanie“, čo znamená, že špecifikácia nemá žiadny vplyv na to, čo sa má stať. Kompilátor sa o tom nemusí ani sťažovať.

Spoločnosť C dlho uprednostňovala poskytnutie moci programátorovi aj na ich vlastné riziko. Kompilátor nezachytí medzery mimo čítania alebo zápisu, pokiaľ výslovne nepovolíte možnosti kompilátora, ktoré ho chránia. A čo viac, je celkom možné, že za behu programu prekročíte hranicu poľa spôsobom, pred ktorým nemôže chrániť ani kontrola kompilátora.

Bežná chyba C: Nekontroluje sa výsledky malloc

malloc a calloc (pre vopred vynulovanú pamäť) sú funkcie knižnice C, ktoré zo systému získavajú haldu alokovanú pamäť. Ak nie sú schopní alokovať pamäť, generujú chybu. V časoch, keď mali počítače relatívne málo pamäte, bola reálna šanca zavolať malloc nemusí byť úspešný.

Aj keď dnes majú počítače gigabajty RAM, stále existuje šanca malloc môže zlyhať, najmä pod vysokým tlakom pamäte alebo pri prideľovaní veľkých dosiek pamäte naraz. To platí najmä pre programy C, ktoré najskôr „rozdelia“ veľký blok pamäte z OS a potom ho rozdelia pre svoje vlastné použitie. Ak toto prvé pridelenie zlyhá, pretože je príliš veľké, môžete toto odmietnutie zachytiť, zmenšiť pridelenie a zodpovedajúcim spôsobom naladiť heuristiku využitia pamäte programu. Ak ale alokovaná pamäť zlyhá, celý program by mohol ísť bruchom hore.

Bežná chyba C: Používanie neplatné * pre všeobecné ukazovatele do pamäte

Použitímneplatné * poukazovať na pamäť je starý zvyk - a zlý zvyk. Ukazovatele na pamäť by mali byť vždy char *, nepodpísaný znak *alebouintptr_t *. Mali by to poskytovať moderné kompilátory C. uintptr_t ako súčasť stdint.h

Ak je označený jedným z týchto spôsobov, je zrejmé, že ukazovateľ odkazuje na pamäťové miesto v abstraktu namiesto na nejaký nedefinovaný typ objektu. To je dvojnásobne dôležité, ak vykonávate ukazovateľovú matematiku. Suintptr_t * a podobne, prvok veľkosti, na ktorý sa poukazuje, a spôsob jeho použitia sú jednoznačné. S neplatné *, nie veľmi.

Vyvarujte sa častých chýb C - 5 tipov

Ako sa môžete vyhnúť týmto príliš častým chybám pri práci s pamäťou, poľami a ukazovateľmi v jazyku C? Majte na pamäti týchto päť tipov.

Štruktúra programov C, aby sa udržalo jasné vlastníctvo pamäte

Ak práve spúšťate aplikáciu C, malo by sa zamyslieť nad spôsobom alokovania a uvoľňovania pamäte ako jedného z organizačných princípov programu. Ak nie je jasné, kde sa dané pridelenie pamäte uvoľní alebo za akých okolností, žiadate o problém. Vyvíjajte ďalšie úsilie na to, aby bolo vlastníctvo pamäte čo najjasnejšie. Urobíte sebe (a budúcim vývojárom) láskavosť.

To je filozofia jazykov ako Rust. Rust znemožňuje správne napísať program, ktorý sa kompiluje, pokiaľ jasne nevyjadríte, ako sa pamäť vlastní a prenáša. C nemá žiadne takéto obmedzenia, ale je rozumné prijať túto filozofiu ako smerodajné svetlo, kedykoľvek je to možné.

Použite možnosti kompilátora C, ktoré chránia pred problémami s pamäťou

Mnoho problémov opísaných v prvej polovici tohto článku možno označiť použitím prísnych možností kompilátora. Posledné vydania gccnapríklad poskytujú nástroje ako AddressSanitizer („ASAN“) ako možnosť kompilácie na kontrolu proti bežným chybám správy pamäte.

Upozorňujeme, že tieto nástroje nezachytia úplne všetko. Sú to mantinely; ak idete v teréne, nechytia sa za volant. Niektoré z týchto nástrojov, napríklad ASAN, tiež ukladajú náklady na kompiláciu a spustenie, preto by sa im pri zostavovaní vydaní malo zabrániť.

Pomocou Cppcheck alebo Valgrind analyzujte C kód na úniky pamäte

Tam, kde samotné kompilátory zaostávajú, zakročia ďalšie nástroje, aby vyplnili medzeru - najmä pokiaľ ide o analýzu správania programu za behu.

Cppcheck spúšťa statickú analýzu zdrojového kódu C, aby zistil bežné chyby v správe pamäte a nedefinované správanie (okrem iného).

Valgrind poskytuje vyrovnávaciu pamäť nástrojov na detekciu chýb pamäte a vlákien pri spustení programov C. Je to oveľa účinnejšie ako pri analýze kompilácie, pretože informácie o správaní programu môžete odvodiť, keď je skutočne aktívny. Nevýhodou je, že program beží pri zlomku svojej bežnej rýchlosti. Ale toto je všeobecne na testovanie v poriadku.

Tieto nástroje nie sú striebornými guľkami a nezachytia všetko. Ale fungujú ako súčasť všeobecnej obrannej stratégie proti zlému spravovaniu pamäte v C.

Automatizujte správu pamäte C pomocou zberača odpadu

Pretože chyby pamäte sú viditeľným zdrojom problémov s C, je tu jedno jednoduché riešenie: Nespravujte pamäť v C ručne. Použite smetiar.

Áno, toto je možné v C. Na pridanie automatickej správy pamäte do programov C môžete použiť niečo ako zberač odpadu Boehm-Demers-Weiser. U niektorých programov môže použitie kolektora Boehm veci ešte urýchliť. Môže sa dokonca použiť ako mechanizmus zisťovania úniku.

Hlavnou nevýhodou zberača odpadu Boehm je, že nemôže skenovať alebo uvoľňovať pamäť, ktorá používa predvolené nastavenie malloc. Používa svoju vlastnú alokačnú funkciu a funguje iba na pamäť, ktorú ste jej alokovali špeciálne.

Nepoužívajte C, keď to bude stačiť v inom jazyku

Niektorí ľudia píšu v jazyku C, pretože ich to skutočne baví a považuje za plodné. Celkovo je však najlepšie používať C iba vtedy, keď musíte, a potom iba striedmo, v niekoľkých situáciách, keď je to naozaj ideálna voľba.

Ak máte projekt, v ktorom bude výkon vykonania obmedzený hlavne vstupom / výstupom alebo prístupom na disk, jeho napísanie v jazyku C ho pravdepodobne nezrýchli spôsobmi, na ktorých záleží, a pravdepodobne iba zvýši pravdepodobnosť jeho výskytu a chyby udržiavať. Rovnaký program by sa dal napísať aj v jazykoch Go alebo Python.

Ďalším prístupom je použitie C. iba pre skutočne náročné na výkon časti aplikácie a spoľahlivejší, aj keď pre ostatné časti pomalší jazyk. Python možno opäť použiť na zabalenie knižníc C alebo vlastného kódu C, čo z neho robí dobrú voľbu pre ďalšie základné komponenty, ako je napríklad spracovanie príkazového riadku.

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