Jeg har lært C ++, og jeg har svært ved at forstå null. Især nævner jeg de læste tutorials, der udfører en “null check”, men jeg er ikke sikker på, hvad det betyder, eller hvorfor det er nødvendigt.
- Hvad er nøjagtigt nul?
- Hvad betyder det at “kontrollere for null”?
- Behøver jeg altid kontrollere for null?
Enhver kodeeksempel vil blive meget værdsat.
Kommentarer
- Hvornår skal du: programmers.stackexchange.com/questions/186036/…
- Jeg vil råde dig til at få nogle bedre tutorials, hvis alle dem, du læser, taler om nullchecks uden nogensinde at forklare dem og give eksempler på kode …
Svar
I C og C ++ er markører i sagens natur usikre, det vil sige, når du foretager en markør, det er dit eget ansvar at sikre, at det peger et eller andet sted gyldigt; dette er en del af, hvad “manuel hukommelsesstyring” handler om (i modsætning til de automatiske hukommelsesstyringsordninger, der er implementeret på sprog som Java , PHP eller .NET-runtime, som ikke giver dig mulighed for at oprette ugyldige referencer uden betydelig indsats).
En almindelig løsning, der fanger mange fejl, er at indstille alle markører, der ikke peger på noget som NULL
(eller i korrekt C ++ 0
), og kontrollere det før adgang til markøren. Specifikt er det almindelig praksis at initialisere alle markører til NULL (medmindre du allerede har noget at pege dem på, når du erklærer dem), og sæt dem til NULL, når du delete
eller free()
dem (medmindre de går uden for rækkevidde umiddelbart efter 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 version:
void fill_foo(int* foo) { if (!foo) { // this is the NULL check printf("This is wrong\n"); return; } *foo = 23; }
Uden nulkontrol vil overførsel af en NULL-markør til denne funktion medføre en segfault, og der er ikke noget, du kan gøre – OS vil simpelthen dræbe din proces og måske kernedumpe eller pop op en dialog med crashrapport. Med nulchecket på plads kan du udføre korrekt fejlhåndtering og gendanne yndefuldt – rette problemet selv, afbryde den aktuelle handling, skrive en logindgang, underrette brugeren, hvad der er passende.
Kommentarer
- @MrLister hvad mener du, nulstjekker fungerer ikke ‘ t i C ++? Du skal bare initialisere markøren til null, når du erklærer den.
- Hvad jeg mener er, du skal huske at indstille markøren til NULL, ellers vinder den ‘ t arbejde. Og hvis du husker, med andre ord, hvis du ved , at markøren er NULL, vil du alligevel ikke have ‘. fill_foo kontrollerer, om markøren har en værdi, ikke om markøren har en gyldig værdi. I C ++ garanteres ikke markører, at de enten har NULL eller har en gyldig værdi.
- En påstand () ville være en bedre løsning her. Der er ‘ ikke noget forsøg på at ” være sikker “. Hvis NULL blev sendt ind, er det ‘ åbenbart forkert, så hvorfor ikke bare nedbrud eksplicit for at gøre programmøren fuldt ud opmærksom? (Og i produktionen betyder det ikke ‘, fordi du ‘ har bevist at ingen vil kalde fill_foo () med NULL, ikke? Det er virkelig ‘ ikke så svært.)
- Don ‘ glem ikke at nævne, at en endnu bedre version af denne funktion skal bruge referencer i stedet for pekere, hvilket gør NULL-kontrollen forældet.
- Dette handler ikke om manuel hukommelsesstyring, og et administreret program vil også sprænge, ( eller hæv i det mindste en undtagelse, ligesom et oprindeligt program vil gøre det på de fleste sprog,) hvis du forsøger at fjerne en null-reference.
Svar
De andre svar dækkede stort set dit nøjagtige spørgsmål. En nulcheck foretages for at være sikker på, at den markør, du modtog, faktisk peger på en gyldig forekomst af en type (objekter, primitiver osv.).
Jeg vil tilføje mit eget råd her, Undgå nulkontrol. 🙂 Nul kontrol (og andre former for defensiv programmering) rodkode op, og gør det faktisk mere udsat for fejl end andre fejlhåndteringsteknikker.
Min yndlingsteknik, når det kommer til objektmarkører er at bruge Null Object mønster . Det betyder at returnere en (pointer – eller endnu bedre, henvisning til en) tom matrix eller liste i stedet for null, eller returnere en tom streng (“”) i stedet for null, eller endda strengen “0” (eller noget svarende til “intet” i sammenhængen), hvor du forventer, at den bliver parset til et heltal.
Som en bonus er her en lille ting, som du måske ikke vidste om nullpegeren, som (først formelt) blev implementeret af CAR Hoare til Algol W-sproget i 1965.
Jeg kalder det min milliarddollarfejl. Det var opfindelsen af null-referencen i 1965. På det tidspunkt designede jeg det første omfattende typesystem til referencer i et objekt orienteret sprog (ALGOL W). Mit mål var at sikre, at al brug af referencer skulle være helt sikker med kontrol udført automatisk af kompilatoren. Men jeg kunne ikke modstå fristelsen til at indsætte en null reference, simpelthen fordi det var sådan let at implementere. Dette har ført til utallige fejl, sårbarheder og systemnedbrud, som sandsynligvis har forårsaget en milliard dollars smerte og skade i de sidste fyrre år.
Kommentarer
- Null Object er endnu værre end bare at have en nul-markør. Hvis en algoritme X kræver data Y, som du ikke har, så er det en fejl i dit program , som du simpelthen skjuler ved at lade som om du gør det.
- Det afhænger af sammenhængen, og uanset hvordan test for ” datatilstedeværelse ” slår test for null i min bog. Fra min erfaring, hvis en algoritme fungerer på f.eks. En liste, og listen er tom, så har algoritmen simpelthen intet at gøre, og det opnår man ved bare at bruge standardkontroludtalelser som for / foreach.
- Hvis algoritmen ikke har noget at gøre, hvorfor kalder du det endda? Og grunden til, at du måske først ville have kaldt det, er fordi det gør noget vigtigt .
- @DeadMG Fordi programmer handler om input og i den virkelige verden, i modsætning til hjemmearbejde, input kan være irrelevant (f.eks. tomt). Kode kaldes stadig på begge måder. Du har to muligheder: enten kontrollerer du for relevans (eller tomhed), eller du designer dine algoritmer, så de læser og fungerer godt uden eksplicit at kontrollere relevans ved hjælp af betingede udsagn.
- Jeg kom her for at komme næsten med samme kommentar, så gav dig min stemme i stedet. Imidlertid vil jeg også tilføje, at dette er repræsentativt for et større problem med zombieobjekter – når som helst du har objekter med initialisering (eller ødelæggelse) i flere trin, der ikke er fuldt levende, men ikke helt døde. Når du ser ” sikker ” kode på sprog uden deterministisk afslutning, der har tilføjet kontrol i hver funktion for at se om objektet er blevet bortskaffet, det er dette generelle problem med at opdrage det ‘ s hoved. Du skal aldrig if-null, du skal arbejde med tilstande, der har de objekter, de har brug for i deres levetid.
Svar
Nullemarkørens værdi repræsenterer en veldefineret “ingensteds”; det er en ugyldig markørværdi, der garanteret sammenligner ulige med nogen anden markørværdi. Forsøg på at afslutte en nulmarkør resulterer i udefineret adfærd og vil normalt føre til en runtime-fejl, så du vil sikre dig, at en markør ikke er NULL, før du forsøger at dereferere den. Et antal C- og C ++ – biblioteksfunktioner returnerer en nul-markør for at indikere en fejltilstand. For eksempel returnerer biblioteksfunktionen malloc
en nul pointerværdi, hvis den ikke kan allokere det antal bytes, der er anmodet om, og forsøg på at få adgang til hukommelse gennem denne markør vil (normalt) føre til en runtime-fejl:
int *p = malloc(sizeof *p * N); p[0] = ...; // this will (usually) blow up if malloc returned NULL
Så vi er nødt til at sørge for, at malloc
-opkaldet lykkes ved at kontrollere værdien af p
mod NULL:
int *p = malloc(sizeof *p * N); if (p != NULL) // or just if (p) p[0] = ...;
Hæng nu på dine sokker et øjeblik, dette får en lidt ujævn.
Der er en null pointer værdi og en null pointer konstant , og de to er ikke nødvendigvis de samme. Nullemarkøren værdi er den værdi, den underliggende arkitektur bruger til at repræsentere “ingensteds”. Denne værdi kan være 0x00000000 eller 0xFFFFFFFF eller 0xDEADBEEF eller noget helt andet. Antag ikke, at nul pointer værdi altid er 0.
Nul pointer konstant , OTOH, er altid et 0-værdig integreret udtryk. For så vidt angår din kildekode repræsenterer 0 (eller ethvert integreret udtryk, der evalueres til 0), en nul-markør. Både C og C ++ definerer NULL-makroen som nulmarkørkonstant. Når din kode er kompileret, erstattes nulmarkøren konstant med den relevante nulmarkør værdi i den genererede maskinkode.
Vær også opmærksom på, at NULL kun er en af mange mulige ugyldige markørværdier; hvis du erklærer en automatisk pointervariabel uden eksplicit at initialisere den, f.eks.
int *p;
er den oprindeligt gemte værdi i variablen ubestemt , og svarer muligvis ikke til en gyldig eller tilgængelig hukommelsesadresse. Desværre er der ingen (bærbar) måde at fortælle, om en ikke-NULL-markørværdi er gyldig eller ikke, før du prøver at bruge den. Så hvis du har at gøre med markører, er det normalt en god ide at eksplicit initialisere dem til NULL når du erklærer dem, og at sætte dem til NULL når de “ikke aktivt peger på noget.
Bemærk, at dette mere er et problem i C end C ++; idiomatisk C ++ burde ikke bruge pegepinde så meget.
Svar
Der er et par metoder, som i det væsentlige gør det samme ting.
int *foo = NULL; //sometimes set to 0x00 or 0 or 0L instead of NULL
null kontrol (kontroller om markøren er null), version A
if( foo == NULL)
null-check, version B
if( !foo ) //since NULL is defined as 0, !foo will return a value from a null pointer
null-check, version C
if( foo == 0 )
Af de tre foretrækker jeg at bruge den første kontrol, da den eksplicit fortæller fremtidige udviklere, hvad du forsøgte at kontrollere, OG det gør det klart, at du forventede, at foo ville være en markør.
Svar
Du behøver ikke. Den eneste grund til at bruge en markør i C ++ er, at du udtrykkeligt vil have tilstedeværelsen af nulmarkører; ellers kan du tage en reference, som både er semantisk lettere at bruge og garanterer ikke-nul.
Kommentarer
- @James: ‘ ny ‘ i kernetilstand?
- @James: En implementering af C ++, der repræsenterer de muligheder, som et betydeligt flertal af C ++ kodere nyder. Det inkluderer alle C ++ 03 sprogfunktioner (undtagen
export
) og alle C ++ 03-biblioteksfunktioner og TR1 og et godt stykke C ++ 11. - Jeg ønsker at folk ikke ville ‘ ikke sige at ” referencer garanterer ikke-null. ” De don ‘ t. Det er lige så let at generere en null-reference som en null-pointer, og de udbredes på samme måde.
- @ Stargazer: Spørgsmålet er 100% overflødigt, når du bare bruger værktøjerne, som sprogdesignerne er gode praksis foreslår, at du skal.
- @DeadMG, det betyder ikke ‘, om det er overflødigt. Du svarede ikke ‘ t besvarede spørgsmålet . Jeg ‘ Jeg siger det igen: -1.
Svar
Hvis du ikke kontrollerer NULL-værdien, specielt hvis det er en markør til en struktur, har du måske mødt en sikkerhedssårbarhed – NULL-pointerdifferens. NULL-pointerdifferens kan føre til nogle andre alvorlige sikkerhedssårbarheder såsom bufferoverløb, race betingelse … der kan tillade angriber at tage kontrol over din computer.
Mange softwareleverandører som Microsoft, Oracle, Adobe, Apple … frigiver softwarepatch for at løse disse sikkerhedssårbarheder. Jeg synes du burde tjek NULL-værdien for hver markør 🙂