Co to znamená provést “ nulovou kontrolu ” v jazyce C nebo C ++?

Učil jsem se C ++ a těžko chápu null. Zejména výukové programy, které jsem četl, zmiňují provádění „nulové kontroly“, ale nejsem si jistý, co to znamená nebo proč je to nutné.

  • Co přesně je null?
  • Co to znamená „zkontrolovat hodnotu null“?
  • Musím vždy zkontrolovat hodnotu null?

Jakékoli příklady kódu by byly velmi oceněny.

Komentáře

Odpověď

V jazycích C a C ++ jsou ukazatele ze své podstaty nebezpečné, tj. když dereferencujete ukazatel, je vaší vlastní odpovědností zajistit, aby to ukazovalo někam platné, to je součást toho, o čem je „manuální správa paměti“ (na rozdíl od automatických schémat správy paměti implementovaných v jazycích, jako je Java) , PHP nebo běhový modul .NET, který vám neumožňuje vytvářet neplatné reference bez značné námahy).

Běžným řešením, které zachytí mnoho chyb, je nastavit všechny ukazatele, které na nic neukazují jako NULL (nebo ve správném jazyce C ++ 0) a zkontrolovat to před přístupem k ukazateli. Konkrétně je běžnou praxí inicializovat všechny ukazatele na NULL (pokud již nemáte, na co je chcete při deklaraci ukázat) a nastavit je na NULL, když delete nebo free() je (pokud bezprostředně poté nezmizí z rozsahu). Příklad (v jazyce C, ale také v platném jazyce C ++):

void fill_foo(int* foo) { *foo = 23; // this will crash and burn if foo is NULL } 

Lepší verze:

void fill_foo(int* foo) { if (!foo) { // this is the NULL check printf("This is wrong\n"); return; } *foo = 23; } 

Bez kontroly nuly předání ukazatele NULL do této funkce způsobí segfault a není nic, co byste mohli udělat – OS jednoduše zabije váš proces a možná core-dump nebo pop-up dialogové okno se zprávou o selhání. Když je zavedena nulová kontrola, můžete provádět správné zpracování chyb a ladně se zotavit – opravit problém sami, přerušit aktuální operaci, napsat záznam do protokolu, upozornit uživatele, ať je to vhodné.

Komentáře

  • @MrLister, co tím myslíte, null kontroluje, zda ‚ nefunguje v C ++? Musíte jen inicializovat ukazatel na null, když jej deklarujete.
  • Chci tím říct, musíte si pamatovat nastavit ukazatel na NULL, jinak zvítězil t práce. A pokud si pamatujete, jinými slovy, pokud víte , že ukazatel má hodnotu NULL, nebudete ‚ stejně muset volat fill_foo. fill_foo zkontroluje, zda má ukazatel hodnotu, ne pokud má ukazatel platnou hodnotu. V C ++ není zaručeno, že by ukazatele byly buď NULL, nebo mají platnou hodnotu.
  • Assert () by zde bylo lepším řešením. ‚ Nemá smysl pokoušet se “ být v bezpečí „. Pokud bylo předáno NULL, je to zjevně špatné ‚, tak proč prostě nepadnout výslovně, aby si programátor plně uvědomil? (A ve výrobě to nezáleží ‚ na tom, protože jste ‚ ve dokázali , že nikdo nebude volat fill_foo () s NULL, že? Opravdu to ‚ není tak těžké.)
  • Nezapomeň ‚ zmínit, že ještě lepší verze této funkce by měla používat odkazy místo ukazatelů, čímž by kontrola NULL byla zastaralá.
  • O tom není ruční správa paměti a spravovaný program vybuchne také, ( nebo alespoň vyvolat výjimku, stejně jako nativní program ve většině jazyků), pokud se pokusíte dereferovat nulový odkaz.

Odpovědět

Ostatní odpovědi do značné míry pokrývaly vaši přesnou otázku. Provede se nulová kontrola, aby se zajistilo, že ukazatel, který jste obdrželi, skutečně ukazuje na platnou instanci typu (objekty, primitiva atd.).

Sem přidám svoji vlastní radu, Vyhněte se nulovým kontrolám. 🙂 Nulové kontroly (a další formy obranného programování) zahlcují kód a ve skutečnosti ho činí náchylnějším k chybám než jiné techniky zpracování chyb.

Moje oblíbená technika, pokud jde o object pointers is to use the Null Object pattern . To znamená vracet (ukazatel – nebo ještě lépe odkaz na) prázdné pole nebo seznam namísto null, nebo vrací prázdný řetězec („“) namísto null, nebo dokonce řetězec „0“ (nebo něco ekvivalentního „nic“ v kontextu), kde očekáváte, že bude analyzován na celé číslo.

Jako bonus je zde něco málo, co jste možná nevěděli o nulovém ukazateli, který (nejprve formálně) implementoval CAR Hoare pro jazyk Algol W v roce 1965.

Říkám tomu moje miliardová chyba. Byl to vynález nulové reference v roce 1965. V té době jsem navrhoval první komplexní typový systém pro reference v objektu orientovaný jazyk (ALGOL W). Mým cílem bylo zajistit, aby veškeré použití odkazů mělo být absolutně bezpečné, s kontrolou prováděnou automaticky kompilátorem. Ale nemohl jsem odolat pokušení vložit nulovou referenci, jednoduše proto, že to tak bylo snadné provedení. To vedlo k nesčetným chybám, zranitelnostem a haváriím systému, které za posledních čtyřicet let pravděpodobně způsobily bolest a poškození miliardy dolarů.

Komentáře

  • Null Object je ještě horší než mít nulový ukazatel. Pokud algoritmus X vyžaduje data Y, která nemáte, jedná se o chybu ve vašem programu , kterou jednoduše skryjete předstíráním, že tak učiníte.
  • Záleží na kontext a v obou případech testování “ přítomnosti dat “ v mé knize překonává testování na hodnotu null. Z mé zkušenosti, pokud algoritmus pracuje na, řekněme, seznamu a seznam je prázdný, pak algoritmus prostě nemá co dělat, a dosahuje toho pouhým použitím standardních řídicích příkazů, jako například for / foreach.
  • Pokud algoritmus nemá nic společného, tak proč jej vůbec nazýváte? A důvod, proč jste to možná chtěli nazvat, je protože dělá něco důležitého .
  • @DeadMG Protože programy jsou o vstupu a ve skutečném světě, na rozdíl domácí úkoly, zadání může být irelevantní (např. prázdné). Kód je stále volán v obou směrech. Máte dvě možnosti: buď zkontrolujete relevanci (nebo prázdnotu), nebo navrhnete své algoritmy tak, aby četly a fungovaly dobře, aniž byste explicitně kontrolovali relevanci pomocí podmíněných příkazů.
  • Přišel jsem sem, abych vytvořil téměř stejný komentář, takže jsem místo toho dal můj hlas. Dodal bych však také, že toto je reprezentativní pro větší problém zombie objektů – kdykoli máte objekty s vícestupňovou inicializací (nebo zničením), které nejsou plně živé, ale nejsou zcela mrtvé. Když uvidíte “ bezpečný “ kód v jazycích bez deterministické finalizace, která přidala kontroly do každé funkce, abyste zjistili, zda byl objekt vyřazen, je to tento obecný problém chovat ji ‚ s hlavou. Nikdy byste neměli, pokud-null, měli byste pracovat se státy, které mají objekty, které potřebují pro svou životnost.

Odpovědět

Hodnota nulového ukazatele představuje dobře definované „nikde“; je to neplatná hodnota ukazatele, u které je zaručeno, že bude nerovnoměrné s jakoukoli jinou hodnotou ukazatele. Pokus o dereferenci nulového ukazatele má za následek nedefinované chování a obvykle povede k běhové chybě, takže se před pokusem o dereferenci chcete ujistit, že ukazatel nemá hodnotu NULL. Řada funkcí knihovny C a C ++ vrátí nulový ukazatel k indikaci chybového stavu. Například funkce knihovny malloc vrátí hodnotu nulového ukazatele, pokud nemůže přidělit požadovaný počet bajtů, a pokus o přístup k paměti prostřednictvím tohoto ukazatele (obvykle) povede k runtime chybě:

int *p = malloc(sizeof *p * N); p[0] = ...; // this will (usually) blow up if malloc returned NULL 

Musíme se tedy ujistit, že volání malloc proběhlo úspěšně kontrolou hodnoty p proti NULL:

int *p = malloc(sizeof *p * N); if (p != NULL) // or just if (p) p[0] = ...; 

Počkejte chvíli na ponožky, dostanete bit hrbolatý.

K dispozici je nulový ukazatel hodnota a nulový ukazatel konstantní a oba nemusí být nutně stejné. Nulový ukazatel hodnota je jakákoli hodnota, kterou základní architektura používá k reprezentaci „nikde“. Tato hodnota může být 0x00000000 nebo 0xFFFFFFFF nebo 0xDEADBEEF nebo něco úplně jiného. Nepředpokládejte, že nulový ukazatel hodnota je vždy 0.

Nulový ukazatel konstanta , OTOH, je vždy integrální výraz s nulovou hodnotou. Pokud jde o váš zdrojový kód , 0 (nebo jakýkoli integrální výraz, který je vyhodnocen jako 0) představuje nulový ukazatel. C i C ++ definují makro NULL jako konstantu nulového ukazatele. Když je váš kód zkompilován, bude nulový ukazatel konstanta nahrazen příslušným nulovým ukazatelem hodnota v generovaném strojovém kódu.

Uvědomte si také, že NULL je pouze jednou z mnoha možných neplatných hodnot ukazatelů; pokud deklarujete proměnnou automatického ukazatele, aniž byste ji explicitně inicializovali, například

int *p; 

hodnota původně uložená v proměnné je neurčitá , a nemusí odpovídat platné nebo přístupné adrese paměti. Bohužel neexistuje (přenosný) způsob, jak zjistit, zda je hodnota ukazatele, která nemá hodnotu NULL, platná nebo ne, než se ji pokusíte použít. Takže pokud máte co do činění s ukazateli, je obvykle dobrý nápad je explicitně inicializovat na NULL, když je deklarujete, a nastavit je na NULL, když na nic aktivně neukazují.

Všimněte si, že se jedná spíše o problém v C než v C ++; idiomatic C ++ by neměl tolik používat ukazatele.

Odpověď

Existuje několik metod, všechny v zásadě dělají totéž věc.

 int *foo = NULL; //sometimes set to 0x00 or 0 or 0L instead of NULL 

kontrola nuly (zkontrolujte, zda je ukazatel nulový), verze A

 if( foo == NULL) 

nulová kontrola, verze B

 if( !foo ) //since NULL is defined as 0, !foo will return a value from a null pointer 

nulová kontrola, verze C

 if( foo == 0 ) 

Ze všech tří dávám přednost použití první kontroly, protože výslovně říká budoucím vývojářům, co jste se pokoušeli zkontrolovat, a dává jasně najevo, že jste očekávali, že foo bude ukazatel.

Odpověď

Nemáte. Jediným důvodem pro použití ukazatele v C ++ je to, že výslovně chcete přítomnost nulových ukazatelů; jinak si můžete vzít odkaz, který je sémanticky jednodušší a zaručuje nenulovou hodnotu.

Komentáře

  • @James: ‚ nový ‚ v režimu jádra?
  • @James: Implementace C ++, která představuje možnosti, které významná většina C ++ programátoři si užívají. To zahrnuje všechny funkce jazyka C ++ 03 (kromě export) a všechny funkce knihovny C ++ 03 a TR1 a dobrá část C ++ 11.
  • Přeji přeji lidem, aby ‚ neřekli, že “ odkazy zaručují nenulovou hodnotu. “ Nedodávají ‚ t. Je stejně snadné vygenerovat nulový odkaz jako nulový ukazatel a šíří se stejným způsobem.
  • @Stargazer: Otázka je 100% nadbytečná, když právě používáte nástroje způsobem, jakým jazykoví návrháři a dobří praxe navrhuje, že byste měli.
  • @DeadMG, nezáleží na tom, zda je to nadbytečné ‚. Na otázku neodpověděl jsi ‚ neodpověděl . ‚ Řeknu to znovu: -1.

Odpovědět

Pokud nezkontrolujete hodnotu NULL, zvláště pokud se jedná o ukazatel na strukturu, možná jste narazili na chybu zabezpečení – dereference ukazatele NULL. Dereference ukazatele NULL může vést k dalším závažným zranitelnostem zabezpečení, jako je přetečení vyrovnávací paměti, stav závodu … to může umožnit útočníkovi převzít kontrolu nad vaším počítačem.

Mnoho dodavatelů softwaru, jako jsou Microsoft, Oracle, Adobe, Apple … vydává softwarovou opravu, která opraví tyto chyby zabezpečení. Myslím, že byste měli zkontrolujte hodnotu NULL každého ukazatele 🙂

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *