Hva betyr det å gjøre en “ nullkontroll ” i C eller C ++?

Jeg har lært C ++ og har vanskelig for å forstå null. Spesielt nevner opplæringen jeg har lest å gjøre en «null check», men jeg er ikke sikker på hva det betyr eller hvorfor det er nødvendig.

  • Hva er egentlig null?
  • Hva betyr det å «sjekke om null»?
  • Må jeg alltid sjekke om det er null?

Eventuelle kodeeksempler vil bli verdsatt.

Kommentarer

Svar

I C og C ++ er pekere i utgangspunktet usikre, det vil si når du refererer en peker, det er ditt eget ansvar å sørge for at det peker et sted gyldig, dette er en del av hva «manuell minnestyring» handler om (i motsetning til de automatiske minnestyringsordningene som er implementert på språk som Java , PHP eller .NET-kjøretiden, som ikke lar deg lage ugyldige referanser uten betydelig innsats).

En vanlig løsning som fanger mange feil er å sette alle pekere som ikke peker på noe som NULL (eller, i riktig C ++, 0), og sjekker om det før du får tilgang til pekeren. Spesielt er det vanlig praksis å initialisere alle pekere til NULL (med mindre du allerede har noe å peke dem på når du erklærer dem), og sette dem til NULL når du delete eller free() dem (med mindre de går utenfor omfanget umiddelbart etter det). Eksempel (i C, men også gyldig C ++):

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

En bedre versjon:

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

Uten nullkontrollen, vil det å gi en NULL-peker inn i denne funksjonen føre til segfault, og det er ingenting du kan gjøre – OS vil bare drepe prosessen din og kanskje kjernedumpe eller dukke opp en krasjrapportdialog. Med nullkontrollen på plass, kan du utføre riktig feilhåndtering og gjenopprette elegant – korrigere problemet selv, avbryte den gjeldende operasjonen, skrive en loggoppføring, varsle brukeren, hva som helst som er passende.

Kommentarer

  • @MrLister hva mener du, null sjekker fungerer ikke ‘ i C ++? Du må bare initialisere pekeren til null når du erklærer den.
  • Det jeg mener er at du må huske å sette pekeren til NULL, ellers vant den ‘ t fungerer. Og hvis du husker, med andre ord hvis du vet at pekeren er NULL, vil du ikke ‘ ikke ha et behov for å ringe fill_foo uansett. fill_foo sjekker om pekeren har en verdi, ikke om pekeren har en gyldig verdi. I C ++ er det ikke garantert at noen peker har en gyldig verdi.
  • En påstand () vil være en bedre løsning her. Det er ‘ ikke noe poeng å prøve å » være trygg «. Hvis NULL ble sendt inn, er det ‘ åpenbart feil, så hvorfor ikke bare krasje eksplisitt for å gjøre programmereren fullstendig oppmerksom? (Og i produksjon spiller det ingen rolle ‘ fordi du ‘ har bevist at ingen vil kalle fill_foo () med NULL, ikke sant? Det er ‘ ikke så vanskelig.)
  • Ikke glem ‘ å nevne at en enda bedre versjon av denne funksjonen skal bruke referanser i stedet for pekere, noe som gjør NULL-sjekken foreldet.
  • Dette handler ikke manuell minneadministrasjon, og et administrert program vil også sprenge, ( eller hev et unntak i det minste, akkurat som et eget program vil gjøre på de fleste språk,) hvis du prøver å referere til en nullreferanse.

Svar

De andre svarene dekket ganske mye det eksakte spørsmålet ditt. Det blir gjort en nullkontroll for å være sikker på at pekeren du mottok faktisk peker på en gyldig forekomst av en type (objekter, primitiver osv.).

Jeg skal legge til mitt eget råd her, Unngå nullkontroller. 🙂 Null sjekker (og andre former for defensiv programmering) rotkoden opp, og gjør den faktisk mer utsatt for feil enn andre feilhåndteringsteknikker. objektpekere er å bruke Null Object mønster . Det betyr å returnere en (peker – eller enda bedre, referanse til en) tom matrise eller liste i stedet for null, eller returnerer en tom streng («») i stedet for null, eller til og med strengen «0» (eller noe som tilsvarer «ingenting» i sammenhengen) der du forventer at den skal analyseres til et helt tall.

Som en bonus, her «er det noe du kanskje ikke visste om nullpekeren, som ble (første gang formelt) implementert av CAR Hoare for Algol W-språket i 1965.

Jeg kaller det min milliarddollarfeil. Det var oppfinnelsen av nullreferansen i 1965. På det tidspunktet designet jeg det første omfattende typesystemet for referanser i et objekt orientert språk (ALGOL W). Målet mitt var å sikre at all bruk av referanser skulle være helt trygg, med kontroll utført automatisk av kompilatoren. Men jeg kunne ikke motstå fristelsen til å sette inn en nullreferanse, bare fordi det var slik enkel å implementere. Dette har ført til utallige feil, sårbarheter og systemkrasj, som sannsynligvis har forårsaket en milliard dollar smerte og skade de siste førti årene.

Kommentarer

  • Null Object er enda verre enn bare å ha en nullpeker. Hvis en algoritme X krever data Y som du ikke har, er det en feil i programmet ditt som du ganske enkelt skjuler ved å late som om du gjør det.
  • Det avhenger av konteksten, og uansett testing for » datatilstedeværelse » slår testing for null i boken min. Fra min erfaring, hvis en algoritme fungerer på, for eksempel, en liste, og listen er tom, så har algoritmen rett og slett ingenting å gjøre, og det oppnår det ved å bare bruke standard kontrolluttalelser som for / foreach.
  • Hvis algoritmen ikke har noe å gjøre, hvorfor kaller du det til og med? Og grunnen til at du kanskje ønsket å kalle det i utgangspunktet er fordi det gjør noe viktig .
  • @DeadMG Fordi programmer handler om input, og i den virkelige verden, i motsetning til lekser, innspill kan være irrelevante (f.eks. tomme). Kode blir fortsatt kalt på begge måter. Du har to alternativer: enten du ser etter relevans (eller tomhet), eller du utformer algoritmene dine slik at de leser og fungerer bra uten eksplisitt å sjekke for relevans ved bruk av betingede utsagn.
  • Jeg kom hit for å lage nesten samme kommentar, så ga deg stemmen min i stedet. Imidlertid vil jeg også legge til at dette er representativt for et større problem med zombieobjekter – når som helst du har objekter med flertrinns initialisering (eller ødeleggelse) som ikke er fullt levende, men ikke helt døde. Når du ser » safe » kode på språk uten deterministisk finalisering som har lagt til kontroller i hver funksjon for å se om objektet har blitt kastet, det er dette generelle problemet å oppdra det ‘ s hode. Du bør aldri hvis-null, du bør jobbe med tilstander som har gjenstandene de trenger for livet.

Svar

Nullpekerverdien representerer et veldefinert «ingensteds»; det er en ugyldig pekerverdi som garantert vil sammenligne ulik med noen annen pekerverdi. Forsøk på å referere en nullpeker resulterer i udefinert oppførsel, og vil vanligvis føre til en kjøretidsfeil, så du vil forsikre deg om at en peker ikke er NULL før du prøver å gjenskille den. Et antall C- og C ++ – biblioteksfunksjoner vil returnere en nullpeker for å indikere en feiltilstand. For eksempel vil biblioteksfunksjonen malloc returnere en nullpekerverdi hvis den ikke kan tildele antall byte som er forespurt, og forsøk på å få tilgang til minne gjennom den pekeren vil (vanligvis) føre til til en kjøretidsfeil:

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

Så vi må sørge for at malloc samtalen lykkes ved å sjekke verdien på p mot NULL:

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

Nå, heng på sokkene dine et øyeblikk, dette kommer til å få en litt humpete.

Det er en nullpeker verdi og en nullpeker konstant , og de to er ikke nødvendigvis de samme. Nullpekeren verdi er hvilken verdi den underliggende arkitekturen bruker for å representere «ingensteds». Denne verdien kan være 0x00000000, eller 0xFFFFFFFF, eller 0xDEADBEEF, eller noe helt annet. Ikke anta at nullpekeren verdi alltid er 0.

Nullpekeren konstant , OTOH, er alltid et integrert uttrykk med 0 verdier. Når det gjelder kildekoden , representerer 0 (eller ethvert integrert uttrykk som evalueres til 0) en nullpeker. Både C og C ++ definerer NULL-makroen som nullpekerkonstant. Når koden din er kompilert, vil nullpekeren konstant erstattes med riktig nullpeker verdi i den genererte maskinkoden.

Vær også oppmerksom på at NULL bare er en av mange mulige ugyldige pekerverdier; hvis du erklærer en automatisk pekervariabel uten å eksplisitt initialisere den, for eksempel

int *p; 

, er verdien som først ble lagret i variabelen ubestemt , og tilsvarer kanskje ikke en gyldig eller tilgjengelig minneadresse. Dessverre er det ingen (bærbar) måte å fortelle om en ikke-NULL-pekerverdi er gyldig eller ikke før du prøver å bruke den. Så hvis du har å gjøre med pekere, er det vanligvis en god ide å eksplisitt initialisere dem til NULL når du erklærer dem, og å sette dem til NULL når de ikke aktivt peker på noe.

Merk at dette er mer et problem i C enn C ++; idiomatisk C ++ burde ikke bruke pekere så mye.

Svar

Det er et par metoder, alle gjør egentlig det samme ting.

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

nullkontroll (sjekk om pekeren er null), versjon A

 if( foo == NULL) 

nullkontroll, versjon B

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

nullkontroll, versjon C

 if( foo == 0 ) 

Av de tre foretrekker jeg å bruke den første sjekken da den eksplisitt forteller fremtidige utviklere hva du prøvde å se etter OG det gjør det klart at du forventet at foo skulle være en peker.

Svar

Du trenger ikke. Den eneste grunnen til å bruke en peker i C ++ er fordi du eksplisitt vil ha tilstedeværelsen av nullpekere; ellers kan du ta en referanse, som både er semantisk enklere å bruke og garanterer ikke-null.

Kommentarer

  • @James: ‘ ny ‘ i kjernemodus?
  • @James: En implementering av C ++ som representerer mulighetene som et betydelig flertall av C ++ kodere liker. Det inkluderer alle C ++ 03 språkfunksjoner (unntatt export) og alle C ++ 03-biblioteksfunksjoner og TR1 og en god del av C ++ 11.
  • Jeg ønsker at folk ikke ville ‘ ikke si at » referanser garanterer ikke-null. » De don ‘ t. Det er like enkelt å generere en nullreferanse som en nullpeker, og de forplanter seg på samme måte.
  • @ Stargazer: Spørsmålet er 100% overflødig når du bare bruker verktøyene slik språkdesignerne og gode praksis foreslår at du burde.
  • @DeadMG, det betyr ikke ‘ om det er overflødig. Du svarte ikke ‘ t svaret på spørsmålet . Jeg ‘ Jeg sier det igjen: -1.

Svar

Hvis du ikke sjekker NULL-verdien, spesielt hvis det er en peker til en struktur, kan du kanskje møte et sikkerhetsproblem – NULL-pekereferanse. NULL-pekereferanse kan føre til noen andre alvorlige sikkerhetsproblemer som bufferoverløp, løpstilstand … som kan tillate at angriper tar kontroll over datamaskinen din.

Mange programvareleverandører som Microsoft, Oracle, Adobe, Apple … slipper programvareoppdatering for å løse disse sikkerhetsproblemene. Jeg tror du burde sjekk NULL-verdien til hver peker 🙂

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *