Mit jelent a “ nullellenőrzés ” elvégzése C vagy C ++ nyelven?

Tanultam C ++ – t, és nehezen értem a null értéket. Különösen az általam olvasott oktatóanyagok említik a “nullellenőrzést”, de nem vagyok biztos benne, hogy ez mit jelent vagy miért szükséges.

  • Mi is pontosan a null?
  • Mit jelent a “nullának ellenőrzése”?
  • Mindig ellenőriznem kell a nullát?

Bármelyik kódpélda nagyra értékelhető.

megjegyzések

Válasz

A C és a C ++ nyelven a mutatók eleve nem biztonságosak, vagyis amikor egy mutatóra hivatkozunk, az ön felelőssége, hogy megbizonyosodjon arról, hogy valahol érvényes-e; ez a “kézi memóriakezelés” része (ellentétben az olyan nyelvű memóriakezelési sémákkal, mint a Java , PHP vagy .NET futtatókörnyezet, amely nem teszi lehetővé érvénytelen hivatkozások létrehozását jelentős erőfeszítések nélkül).

Gyakori megoldás, amely sok hibát elkap, az összes olyan mutató beállítása, amelyek nem mutatnak semmire. mint NULL (vagy helyesen C ++, 0), és ezt ellenőrizze, mielőtt a mutatóhoz hozzáférne. Konkrétan bevett gyakorlat az összes mutató inicializálása a NULL-ra (kivéve, ha már van mire mutatnia, amikor deklarálja őket), és NULL-ra állítja, amikor delete vagy free() őket (hacsak nem lépnek ki azonnal a hatálya alól). Példa (C-ben, de érvényes C ++ nyelven is):

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

Jobb verzió:

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

Nullellenőrzés nélkül a NULL mutató átengedése ebbe a funkcióba segfaultot okoz, és semmit sem tehet – az operációs rendszer egyszerűen megöli a folyamatot, és esetleg core-dump vagy felbukkan egy összeomlási jelentés párbeszédpanel. A nullellenőrzéssel megfelelő hibakezelést hajthat végre, és kecsesen helyreállíthatja – maga kijavíthatja a problémát, megszakíthatja az aktuális műveletet, naplóbejegyzést írhat, értesítheti a felhasználót, ami megfelelő.

Megjegyzések

  • @MrLister mire gondolsz, a null ellenőrzések nem működnek a C ++ nyelven?

Csak ki kell inicializálnia a mutatót, amikor deklarálja.

  • Amire gondolok, emlékeznie kell arra, hogy a mutatót NULL értékre állította, különben ‘ t munka. És ha emlékszik, más szóval, ha tudja , hogy a mutató NULL, akkor ‘ akkor sem lesz szüksége a fill_foo meghívására. A fill_foo ellenőrzi, hogy a mutatónak van-e értéke, és nem akkor, ha a mutatónak van érvényes értéke. A C ++ nyelven a mutatók sem garantáltan NULL értékűek, és érvényes értékkel rendelkeznek.
  • Itt jobb megoldás lenne egy állítás (). ‘ nincs értelme megpróbálni ” biztonságban lenni “. Ha a NULL-t átadták, akkor ‘ nyilvánvalóan helytelen, akkor miért nem csak úgy összeomlik, hogy a programozó teljesen tudatában legyen? (És a gyártásban nem számít ‘ ez, mert ‘ ve bebizonyította , hogy senki sem fogja hívni a fill_foo-t () a NULL-szal, igaz? Tényleg, ez nem annyira nehéz. div id = “54ac8d8851”> .)
  • Ne felejtsd el ‘ megemlítve, hogy ennek a függvénynek egy még jobb verziójának hivatkozásokat kell használnia a mutatók helyett, ami a NULL ellenőrzést elavulttá teszi.
  • Nem erről szól a kézi memóriakezelés, és egy kezelt program is fel fog robbantani, ( vagy legalábbis emeljen kivételt, csakúgy, mint egy natív program a legtöbb nyelvben,) ha null hivatkozást próbál meg levonni.
  • Válasz

    A többi válasz nagyjából a pontos kérdésedre terjedt ki. Null ellenőrzést hajtunk végre, hogy megbizonyosodjon arról, hogy a kapott mutató valóban egy típus érvényes példányára mutat-e (objektumok, primitívek stb.).

    Saját tanácsot adok ide, Ne kerülje el a semmilyen ellenőrzést. 🙂 A semmilyen ellenőrzés (és a védekező programozás egyéb formái) elrontják a kódot, és valójában hibára hajlamosabbá teszik, mint más hibakezelési technikák.

    Kedvenc technikám, amikor az objektummutatóknak a Null Object minta használatát kell használni. Ez azt jelenti, hogy null helyett egy (mutató – vagy még jobb, ha egy) üres tömböt vagy listát adunk vissza, vagy üres karakterláncot (“”) ad vissza null helyett, vagy akár a “0” karakterláncot (vagy a kontextusban a “semmivel” egyenértékűet), ahol azt várjuk, hogy egész számra elemezzük.

    Bónuszként itt van valami, amit talán nem is tudtál a null mutatóról, amelyet (először hivatalosan) a CAR Hoare hajtott végre az Algol W nyelv számára 1965-ben.

    Milliárdos hibámnak nevezem. A null referencia feltalálása volt 1965-ben. Akkoriban az első átfogó típusú rendszert terveztem egy objektum referenciáira orientált nyelv (ALGOL W). Célom az volt, hogy biztosítsam a referenciák minden használatának teljes biztonságát, az ellenőrzést automatikusan a fordító végezze. De nem tudtam ellenállni a kísértésnek, hogy null referenciát adjak be, egyszerűen azért, mert így volt könnyen kivitelezhető. Ez számtalan hibához, sebezhetőséghez és rendszerösszeomláshoz vezetett, amelyek valószínűleg egymilliárd dollár fájdalmat és kárt okoztak az elmúlt negyven évben.

    Megjegyzések

    • A Null Object még rosszabb annál, hogy csak null mutatóval rendelkezzen. Ha egy X algoritmushoz olyan Y adatokra van szükség, amelyek nincsenek, akkor ez egy hiba a programban , amelyet egyszerűen elrejt, úgy téve, mintha megtenné.
    • Ez attól függ, a kontextus és az ” adat jelenlétének ” tesztelése mindkét esetben felülmúlja a könyvemben található null tesztet. Tapasztalatom szerint, ha egy algoritmus mondjuk egy listán működik, és a lista üres, akkor az algoritmusnak egyszerűen nincs mit tennie, és ezt csak standard vezérlő utasítások, például for / foreach használatával valósítja meg.
    • Ha az algoritmusnak nincs köze, akkor miért is hívod? És annak oka, hogy eleve hívni akartad, mert valami fontosat csinál.
    • @DeadMG Mivel a programok a bemenetről szólnak, és a való világban, ellentétben házi feladatok, a bevitel lényegtelen lehet (pl. üres). A kódot így is hívják, akárhogy is. Két lehetősége van: vagy ellenőrizze a relevanciát (vagy az ürességet), vagy megtervezi az algoritmusokat úgy, hogy azok olvassanak és jól működjenek anélkül, hogy feltételes utasításokkal kifejezetten ellenőriznék a relevanciát.
    • Azért jöttem, hogy szinte a ugyanaz a hozzászólás, ezért helyette a szavazatomat adta meg neked. Ugyanakkor azt is hozzátenném, hogy ez a zombi tárgyak nagyobb problémáját reprezentálja – bármikor vannak olyan többlépcsős inicializálással (vagy megsemmisítéssel) rendelkező objektumok, amelyek még nem élnek, de nem is halottak. Amikor meglátja a ” biztonságos ” kódot olyan nyelveken, ahol nincs determinisztikus véglegesítés, és minden funkcióban ellenőrizte, hogy az objektumot eldobták-e, ez az általános probléma ‘ fejének nevelése. Soha nem szabad if-null, hanem olyan állapotokkal kell dolgoznia, amelyek rendelkeznek az életük során szükséges objektumokkal.

    Válasz

    A null mutató értéke pontosan definiált “sehol”; ez egy érvénytelen mutatóérték, amely garantáltan összehasonlítja az egyenlőtlenséget bármely más mutatóértékkel. A nullmutató eltérítésének megkísérlése meghatározatlan viselkedést eredményez, és általában futásidejű hibához vezet, ezért meg kell győződnie arról, hogy a mutató nem NULL, mielőtt megpróbálja levonni a figyelmét. Számos C és C ++ könyvtárfüggvény nullpontot ad vissza, jelezve a hiba állapotát. Például az malloc könyvtárfüggvény nullapontértéket ad vissza, ha nem tudja kiosztani a kért bájtok számát, és ha a memórián keresztül próbál meg elérni memóriát, akkor ez (általában) vezet futásidejű hibára:

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

    Tehát meg kell győződnünk arról, hogy az malloc hívás sikeres volt-e a p a NULL ellen:

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

    Most egy percig lógjon a zokniján, ez egy kicsit göröngyös.

    Van null mutató érték és egy null mutató állandó , és a kettő nem feltétlenül azonos. A null mutató értéke az az érték, amelyet az alapul szolgáló architektúra a “sehol” kifejezésre használ. Ez az érték lehet 0x00000000, vagy 0xFFFFFFFF, vagy 0xDEADBEEF, vagy valami egészen más. Ne feltételezzük, hogy a null mutató értéke mindig 0.

    A null mutató konstans , az OTOH mindig 0 értékű integrál kifejezés. Ami a forráskód ot illeti, a 0 (vagy bármely integrált kifejezés, amely 0-ra értékel) nullpontot jelent. A C és a C ++ is a NULL makrót definiálja null mutató konstansként. A kód összeállításakor a null mutató konstans helyére a megfelelő null mutató érték kerül a létrehozott gépi kódban.

    Ne feledje továbbá, hogy a NULL csak egy a sok lehetséges érvénytelen mutató érték közül; ha deklarál egy automatikus mutató változót anélkül, hogy kifejezetten inicializálná, például

    int *p; 

    a változóban eredetileg tárolt érték határozatlan , és előfordulhat, hogy nem felel meg érvényes vagy hozzáférhető memóriacímnek. Sajnos nincs (hordozható) módja annak megállapítására, hogy egy nem NULL mutató értéke érvényes-e vagy sem, mielőtt megkísérelné használni. Tehát, ha a mutatókkal foglalkozik, akkor általában jó, ha kifejezetten inicializálja őket arra, hogy NULL, amikor kijelenti őket, és NULL értékre állítja őket, amikor nem mutatnak aktívan semmire.

    Vegye figyelembe, hogy ez inkább a C, mint a C ++ probléma; az idiomatikus C ++ nem használhat annyi mutatót.

    Válasz

    Van pár módszer, amelyek lényegében ugyanazt teszik dolog.

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

    null ellenőrzés (ellenőrizze, hogy a mutató null-e), A verzió

     if( foo == NULL) 

    null ellenőrzés, B verzió

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

    null ellenőrzés, C verzió

     if( foo == 0 ) 

    A három közül inkább az első ellenőrzést használom, mivel ez kifejezetten megmondja a jövőbeli fejlesztőknek, hogy mit próbáltál ellenőrizni ÉS egyértelművé teszi, hogy a foo-ra számítottál, hogy mutató lesz.

    Válasz

    Nem. Az egyetlen oka annak, hogy egy mutatót használjon a C ++ – ban, mert kifejezetten nullpontok jelenlétét szeretné elérni; máskülönben vehet egy referenciát, amely szemantikailag könnyebben használható és garantálja a nem null értéket.

    Megjegyzések

    • @James: ‘ new ‘ kernel módban?
    • @James: A C ++ megvalósítása, amely a C ++ jelentős többségének képességeit képviseli a kódolók élvezik. Ez magában foglalja a összes C ++ 03 nyelvi jellemzőt (kivéve az export) és a C ++ 03 könyvtár összes funkcióját és TR1 és egy jó darab C ++ 11.
    • Szeretném bárcsak az emberek nem mondanák

    ezt ” hivatkozások nem nullát garantálnak. ” Nem ‘ t. Ugyanolyan könnyű null referenciát generálni, mint egy null mutatót, és ugyanúgy terjednek.

  • @Stargazer: A kérdés 100% -ban felesleges, ha csak úgy használja az eszközöket, ahogyan a nyelvtervezők és jó a gyakorlat azt javasolja, hogy tegye.
  • @DeadMG, nem számít ‘, hogy felesleges-e. nem ‘ nem válaszolt a kérdésre . ‘ még egyszer elmondom: -1.
  • Válasz

    Ha nem ellenőrzi a NULL értéket, különösképpen, ha ez egy struktúra mutatója, akkor előfordulhat, hogy találkozott egy biztonsági réssel – NULL pointer dereference. A NULL pointer dereference további súlyos biztonsági résekhez vezethet, például puffertúlcsorduláshoz, versenyfeltétel … ez lehetővé teheti a támadók számára, hogy átvegyék az irányítást a számítógép felett.

    Számos szoftvergyártó, például a Microsoft, az Oracle, az Adobe, az Apple … kiad egy szoftverjavítást a biztonsági rések kijavításához. ellenőrizze az egyes mutatók NULL értékét 🙂

    Vélemény, hozzászólás?

    Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük