Vad betyder det att göra en “ nollkontroll ” i C eller C ++?

Jag har lärt mig C ++ och jag har svårt att förstå null. I synnerhet nämner de självstudier jag har läst att göra en ”null check”, men jag är inte säker på vad det betyder eller varför det är nödvändigt.

  • Vad är exakt null?
  • Vad betyder det att ”kontrollera för null”?
  • Behöver jag alltid kontrollera om det är null?

Eventuella kodexempel skulle vara mycket uppskattade.

Kommentarer

Svar

I C och C ++ är pekare i sig osäkra, det vill säga när du avläser en pekare, det är ditt eget ansvar att se till att det pekar någonstans giltigt; detta är en del av vad ”manuell minneshantering” handlar om (i motsats till de automatiska minneshanteringsscheman som implementeras på språk som Java , PHP eller .NET-körningen, som inte tillåter dig att skapa ogiltiga referenser utan betydande ansträngning).

En vanlig lösning som fångar många fel är att ställa in alla pekare som inte pekar på någonting som NULL (eller, i rätt C ++, 0), och kontrollera det innan du går åt pekaren. Specifikt är det vanligt att initialisera alla pekare till NULL (såvida du inte redan har något att peka på dem när du deklarerar dem) och ställa in dem till NULL när du delete eller free() dem (såvida de inte går utanför tillämpningsområdet omedelbart efter det). Exempel (i C, men även giltigt C ++):

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

En bättre version:

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

Utan nollkontrollen, om du skickar en NULL-pekare till den här funktionen kommer det att leda till en segfault, och det finns inget du kan göra – operativsystemet dödar helt enkelt din process och kanske kärndumpar eller dyker upp en kraschdialog. Med nollkontrollen på plats kan du utföra korrekt felhantering och återställa graciöst – korrigera problemet själv, avbryta den aktuella åtgärden, skriva en loggpost, meddela användaren, vad som helst som är lämpligt.

Kommentarer

  • @MrLister vad menar du, nollkontroller fungerar inte ’ i C ++? Du måste bara initiera pekaren till null när du förklarar den.
  • Vad jag menar är att du måste komma ihåg att ställa in pekaren på NULL eller så vann den ’ t fungerar. Och om du kommer ihåg, med andra ord om du vet att pekaren är NULL, kommer du inte att ’ inte behöver ringa fill_foo ändå. fill_foo kontrollerar om pekaren har ett värde, inte om pekaren har ett giltigt värde. I C ++ garanteras inte pekare antingen NULL eller har ett giltigt värde.
  • Ett påstående () skulle vara en bättre lösning här. Det finns ’ ingen anledning att försöka ” vara säkra ”. Om NULL skickades in är det ’ uppenbarligen fel, så varför inte bara krascha uttryckligen för att göra programmeraren helt medveten? (Och i produktion betyder det inte ’, eftersom du ’ har bevisat att ingen kommer att ringa fill_foo () med NULL, eller hur? ’ är inte så svårt.)
  • Glöm inte ’ att nämna att en ännu bättre version av den här funktionen bör använda referenser istället för pekare, vilket gör NULL-kontrollen föråldrad.
  • Det handlar inte om manuell minneshantering och ett hanterat program kommer också att sprängas, ( eller höj åtminstone ett undantag, precis som ett inbyggt program kommer att göra på de flesta språk,) om du försöker göra en referens till en null referens. = ”answer”>

    De andra svaren täckte ganska mycket din exakta fråga. En nollkontroll görs för att vara säker på att pekaren du fick faktiskt pekar på en giltig instans av en typ (objekt, primitiva osv.).

    Jag ska lägga till mitt eget råd här, Undvik nollkontroller. 🙂 Nollkontroller (och andra former av defensiv programmering) stör uppkoden och gör den faktiskt mer felbenägen än andra felhanteringstekniker.

    Min favoritteknik när det gäller objektpekare är att använda Noll-objektmönster . Det betyder att du returnerar en (pekare – eller ännu bättre, referens till en) tom matris eller lista istället för null, eller returnera en tom sträng (””) istället för null, eller till och med strängen ”0” (eller något som motsvarar ”ingenting” i sammanhanget) där du förväntar dig att den ska analyseras till ett heltal.

    Som en bonus är det här lite du kanske inte visste om nullpekaren, som (först formellt) implementerades av CAR Hoare för Algol W-språket 1965.

    Jag kallar det mitt miljardfel. Det var uppfinningen av nollreferensen 1965. Vid den tiden designade jag det första omfattande typsystemet för referenser i ett objekt orienterat språk (ALGOL W). Mitt mål var att se till att all användning av referenser skulle vara helt säker, med kontroll utförd automatiskt av kompilatorn. Men jag kunde inte motstå frestelsen att sätta in en null referens, helt enkelt för att det var så lätt att implementera. Detta har lett till otaliga fel, sårbarheter och systemkrascher, som antagligen har orsakat en miljard dollar smärta och skador under de senaste fyrtio åren.

    Kommentarer

    • Null Object är ännu värre än att bara ha en nollpekare. Om en algoritm X kräver data Y som du inte har, är det ett fel i ditt program som du helt enkelt gömmer genom att låtsas att du gör det.
    • Det beror på sammanhanget, och på något sätt testar ” datanärvaro ” slår testning för null i min bok. Enligt min erfarenhet, om en algoritm fungerar på, säg, en lista och listan är tom, så har algoritmen helt enkelt inget att göra, och det åstadkommer det genom att bara använda standardkontrolluttalanden som för / förut.
    • Om algoritmen inte har något att göra, varför ringer du det ens? Och anledningen till att du kanske ville kalla det i första hand är eftersom det gör något viktigt .
    • @DeadMG Eftersom program handlar om input, och i den verkliga världen, till skillnad från hemuppgifter, input kan vara irrelevant (t.ex. tom). Koden kallas fortfarande åt båda hållen. Du har två alternativ: antingen kontrollerar du relevans (eller tomhet) eller utformar dina algoritmer så att de läser och fungerar bra utan att uttryckligen söka efter relevans med villkorliga uttalanden.
    • Jag kom hit för att göra nästan samma kommentar, så gav dig min röst istället. Jag vill dock tillägga att detta är representativt för ett större problem med zombieobjekt – när du har objekt med initialisering (eller förstörelse) i flera steg som inte är helt levande men inte riktigt döda. När du ser ” säker ” kod på språk utan deterministisk finalisering som har lagt till kontroller i varje funktion för att se om objektet har kastats, det är detta allmänna problem att uppfostra det ’ s huvud. Du ska aldrig if-null, du borde arbeta med tillstånd som har de föremål de behöver för sin livstid.

Svar

Nollpekarens värde representerar ett väldefinierat ”ingenstans”; det är ett ogiltigt pekervärde som garanterat jämför ojämnt med något annat pekervärde. Att försöka dereferera en nollpekare resulterar i odefinierat beteende och kommer vanligtvis att leda till ett runtime-fel, så du vill se till att en pekare inte är NULL innan du försöker dereferera den. Ett antal C- och C ++ – biblioteksfunktioner returnerar en nollpekare för att indikera ett felvillkor. Biblioteksfunktionen malloc returnerar till exempel ett nullpekervärde om den inte kan allokera antalet byte som har begärts, och försök att komma åt minne genom den pekaren leder (vanligtvis) till ett körtidsfel:

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

Så vi måste se till att malloc -samtalet lyckades genom att kontrollera värdet på p mot NULL:

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

Nu, häng på dina strumpor en minut, detta kommer att få en lite ojämn.

Det finns ett nollpekare värde och en nollpekare konstant och de två är inte nödvändigtvis desamma. Nollpekaren värde är det värde som den underliggande arkitekturen använder för att representera ”ingenstans”. Det här värdet kan vara 0x00000000, eller 0xFFFFFFFF, eller 0xDEADBEEF, eller något helt annat. Antag inte att nollpekaren värde alltid är 0.

Nollpekaren konstant , OTOH, är alltid ett 0-värderat integralt uttryck. När det gäller din källkod representerar 0 (eller något integrerat uttryck som utvärderas till 0) en nollpekare. Både C och C ++ definierar NULL-makrot som nollpekarkonstant. När din kod sammanställs kommer nollpekaren konstant att ersättas med lämpligt nollpekare värde i den genererade maskinkoden.

Tänk också på att NULL bara är ett av många möjliga ogiltiga pekervärden; om du förklarar en automatisk pekvariabel utan att uttryckligen initiera den, till exempel

int *p; 

är värdet som ursprungligen lagrats i variabeln obestämt , och kanske inte motsvarar en giltig eller tillgänglig minnesadress. Tyvärr finns det inget (bärbart) sätt att avgöra om ett icke-NULL-pekervärde är giltigt eller inte innan du försöker använda det. Så om du har att göra med pekare är det vanligtvis en bra idé att uttryckligen initiera dem till NULL när du förklarar dem och att sätta dem till NULL när de inte aktivt pekar på någonting.

Observera att detta är mer ett problem i C än C ++; idiomatisk C ++ borde inte använda pekare så mycket.

Svar

Det finns ett par metoder, alla gör i princip samma sak.

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

nollkontroll (kontrollera om pekaren är noll), version A

 if( foo == NULL) 

nollkontroll, version B

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

nollkontroll, version C

 if( foo == 0 ) 

Av de tre föredrar jag att använda den första kontrollen eftersom den uttryckligen berättar för framtida utvecklare vad du försökte leta efter OCH det gör det klart att du förväntade dig att foo skulle vara en pekare.

Svara

Du behöver inte. Den enda anledningen till att använda en pekare i C ++ är att du uttryckligen vill ha nollpekare. annars kan du ta en referens, som både är semantiskt lättare att använda och garanterar icke-null.

Kommentarer

  • @James: ’ ny ’ i kärnläge?
  • @James: En implementering av C ++ som representerar de funktioner som en betydande majoritet av C ++ kodare tycker om. Det inkluderar alla C ++ 03-språkfunktioner (utom export) och alla C ++ 03-biblioteksfunktioner och TR1 och en bra bit av C ++ 11.
  • Jag vill önskar att folk inte ’ inte säger att ” referenser garanterar icke-null. ” De

t. Det är lika enkelt att generera en null-referens som en null-pekare, och de sprids på samma sätt.

  • @ Stargazer: Frågan är 100% överflödig när du bara använder verktygen som språkdesignerna och bra praxis föreslår att du ska.
  • @DeadMG, det spelar ingen roll ’ om det är överflödigt. Du svarade inte ’ t svaret på frågan . Jag ’ Jag säger det igen: -1.
  • Svar

    Om du inte kontrollerar NULL-värdet, speciellt om det är en pekare till en struktur, kan du kanske möta en säkerhetsproblem – NULL-pekardereferens. NULL-pekardereferens kan leda till några andra allvarliga säkerhetsproblem såsom buffertöverflöd, rasförhållande … som kan tillåta angripare att ta kontroll över din dator.

    Många programvaruleverantörer som Microsoft, Oracle, Adobe, Apple … släpper programvarupatch för att åtgärda dessa säkerhetsproblem. kontrollera NULL-värdet för varje pekare 🙂

    Lämna ett svar

    Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *