Programování

4 běžné chyby programování v C - a 5 tipů, jak se jim vyhnout

Jen málo programovacích jazyků odpovídá C pro naprostou rychlost a výkon na úrovni stroje. Toto tvrzení platilo před 50 lety a je tomu tak i dnes. Existuje však důvod, proč programátoři vytvořili termín „footgun“ pro popis druhu síly C. Pokud si nedáte pozor, může vám C sfouknout prsty - nebo někoho jiného.

Zde jsou čtyři nejčastější chyby, kterých se můžete v C dopustit, a pět kroků, kterými můžete zabránit.

Častá chyba C: neuvolňování malloc-ed paměť (nebo uvolnění více než jednou)

To je jedna z velkých chyb v C, z nichž mnohé zahrnují správu paměti. Přidělená paměť (provádí se pomocí malloc Funkce) není automaticky zlikvidována v C. Je úkolem programátora zlikvidovat tuto paměť, když ji již nepoužívá. Nepodaří se vám uvolnit opakované požadavky na paměť a skončíte s únikem paměti. Zkuste použít oblast paměti, která je již uvolněna, a váš program se zhroutí - nebo, hůře, ochabne a stane se zranitelným vůči útoku pomocí tohoto mechanismu.

Všimněte si, že paměť unikat by měl popisovat pouze situace, kdy je paměť předpokládal být osvobozen, ale není. Pokud program stále přiděluje paměť, protože paměť je skutečně potřebná a používá se pro práci, pak může být její využití pamětineefektivní, ale přísně vzato to není únik.

Častá chyba C: Čtení pole mimo hranice

Tady máme ještě jednu z nejběžnějších a nejnebezpečnějších chyb v C. Čtení za koncem pole může vrátit nesmyslná data. Zápis za hranice pole může poškodit stav programu nebo jej úplně zhroutit, nebo, co je nejhorší, stát se vektorem útoku malwaru.

Proč tedy břemeno kontroly hranic pole ponechává programátorovi? V oficiální specifikaci C je čtení nebo zápis pole za jeho hranice „nedefinované chování“, což znamená, že spec nemá co říci, co se má stát. Kompilátor na to nemusí ani stěžovat.

C dlouho upřednostňoval předávání moci programátorovi i na jejich vlastní riziko. Kompilátor obvykle nezachytí meze čtení nebo zápisu, pokud výslovně nepovolíte možnosti kompilátoru, abyste se proti němu chránili. A co víc, je dobře možné překročit hranici pole za běhu způsobem, proti kterému se nemůže chránit ani kontrola kompilátoru.

Častá chyba C: Nekontroluje se výsledky malloc

malloc a calloc (pro předem vynulovanou paměť) jsou funkce knihovny C, které ze systému získávají paměť přidělenou haldě. Pokud nejsou schopni alokovat paměť, generují chybu. V dobách, kdy počítače měly relativně málo paměti, byla férová šance na volání malloc nemusí být úspěšný.

I když dnes mají počítače gigabajty paměti RAM, stále existuje šance malloc může selhat, zejména pod vysokým tlakem paměti nebo při přidělování velkých desek paměti najednou. To platí zejména pro programy C, které nejprve „přidělují“ velký blok paměti z OS a poté jej pro své vlastní použití rozdělí. Pokud tato první alokace selže, protože je příliš velká, můžete toto odmítnutí zachytit, zmenšit alokaci a podle toho naladit heuristiku využití paměti programu. Pokud se ale alokace paměti nezdaří, celý program by se mohl zvednout.

Častá chyba C: Používání neplatné * pro obecné ukazatele do paměti

Použitímneplatné * poukazovat na paměť je starý zvyk - a špatný. Ukazatele na paměť by měly být vždy char *, nepodepsaný znak *nebouintptr_t *. Měly by poskytovat moderní kompilátory C. uintptr_t jako část stdint.h

Když je označen jedním z těchto způsobů, je jasné, že ukazatel odkazuje na paměťové místo v abstraktu místo na nějaký nedefinovaný typ objektu. To je dvojnásob důležité, pokud provádíte ukazovací matematiku. Suintptr_t * a podobně, na který je odkazován prvek velikosti a jak bude použit, jsou jednoznačné. S neplatné *, ne tak moc.

Vyvarujte se běžných chyb C - 5 tipů

Jak se můžete vyhnout těmto příliš častým chybám při práci s pamětí, poli a ukazateli v jazyce C? Mějte na paměti těchto pět tipů.

Struktura programů C tak, aby vlastnictví paměti zůstalo čisté

Pokud právě spouštíte aplikaci C, stojí za to přemýšlet o způsobu alokace a uvolňování paměti jako jednoho z organizačních principů programu. Pokud není jasné, kde se dané přidělení paměti uvolní nebo za jakých okolností, žádáte o potíže. Vyvíjejte další úsilí, aby bylo vlastnictví paměti co nejjasnější. Uděláte sobě (a budoucím vývojářům) laskavost.

To je filozofie jazyků jako Rust. Rust znemožňuje správně psát program, který se kompiluje, pokud jasně nevyjádříte, jak je paměť vlastněna a přenášena. C nemá žádná taková omezení, ale je rozumné přijmout tuto filozofii jako vodítko, kdykoli je to možné.

Použijte možnosti kompilátoru C, které chrání před problémy s pamětí

Mnoho problémů popsaných v první polovině tohoto článku lze označit pomocí přísných možností kompilátoru. Nedávná vydání gccnapříklad poskytují nástroje jako AddressSanitizer („ASAN“) jako možnost kompilace pro kontrolu proti běžným chybám správy paměti.

Upozorňujeme, že tyto nástroje nezachytí úplně všechno. Jsou to zábradlí; pokud se vydáte do terénu, nechytí se volantu. Některé z těchto nástrojů, například ASAN, také ukládají náklady na kompilaci a běhové prostředí, takže je třeba se jim v sestaveních vydání vyhnout.

Použijte Cppcheck nebo Valgrind k analýze kódu C na únik paměti

Tam, kde samotní kompilátoři nedosahují, zaplní tuto mezeru další nástroje - zejména pokud jde o analýzu chování programu za běhu.

Cppcheck spouští statickou analýzu zdrojového kódu C, aby vyhledal běžné chyby ve správě paměti a nedefinované chování (mimo jiné).

Valgrind poskytuje mezipaměť nástrojů pro detekci chyb paměti a podprocesů při spouštění programů C. To je mnohem výkonnější než použití analýzy v době kompilace, protože můžete odvodit informace o chování programu, když je skutečně aktivní. Nevýhodou je, že program běží na zlomek své normální rychlosti. Ale to je obecně v pořádku pro testování.

Tyto nástroje nejsou stříbrné kulky a nezachytí všechno. Ale fungují jako součást obecné obranné strategie proti špatnému řízení paměti v C.

Automatizujte správu paměti C pomocí garbage collectoru

Protože chyby paměti jsou viditelným zdrojem problémů s C, je tu jedno snadné řešení: Nespravujte paměť v C ručně. Použijte sběrač odpadků.

Ano, toto je možné v C. Pro přidání automatické správy paměti do programů C můžete použít něco jako garbage collector Boehm-Demers-Weiser. U některých programů může použití sběrače Boehm dokonce věci urychlit. Může být dokonce použit jako mechanismus detekce úniku.

Hlavní nevýhodou sběrače odpadků Boehm je, že nemůže skenovat nebo uvolňovat paměť, která používá výchozí malloc. Používá vlastní alokační funkci a funguje pouze v paměti, kterou s ní konkrétně alokujete.

Nepoužívejte C, když bude fungovat jiný jazyk

Někteří lidé píší v jazyce C, protože je to opravdu baví a je to pro ně plodné. Celkově je ale nejlepší používat C pouze tehdy, když musíte, a pak jen střídmě, pro několik situací, kdy je to opravdu ideální volba.

Pokud máte projekt, kde bude výkon provádění omezen hlavně vstupem / výstupem nebo přístupem na disk, jeho napsání v jazyce C pravděpodobně nezrychlí způsoby, na kterých záleží, a pravděpodobně to jen zvýší náchylnost k chybám a bude obtížné udržovat. Stejný program by mohl být napsán v Go nebo Pythonu.

Dalším přístupem je použití C pouze pro skutečně náročné na výkon části aplikace a spolehlivější, i když pomalejší jazyk pro ostatní části. Python lze znovu použít k zabalení knihoven C nebo vlastního kódu C, což z něj činí dobrou volbu pro další základní komponenty, jako je zpracování možností příkazového řádku.

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