En rask og grundig guide til "null": hva det er, og hvordan du bør bruke det (2023)

/ #Programmering
En rask og grundig guide til "null": hva det er, og hvordan du bør bruke det (1)

av Christian Neumanns

Hva er betydningen avnull? Hvordan ernullimplementert? Når bør du brukenulli kildekoden, og når bør duikkebruk det?

En rask og grundig guide til "null": hva det er, og hvordan du bør bruke det (2)

Introduksjon

nuller et grunnleggende konsept i mange programmeringsspråk. Det er allestedsnærværende i alle typer kildekode skrevet på disse språkene. Så det er viktig å fullt ut forstå ideen omnull. Vi må forstå dens semantikk og implementering, og vi må vite hvordan vi skal brukenulli kildekoden vår.

Kommentarer i programmererfora avslører noen ganger litt forvirring mednull. Noen programmerere prøver til og med å unngå detnull. Fordi de tenker på det som "million-dollar feilen", et begrep laget av Tony Hoare, oppfinneren avnull.

Her er et enkelt eksempel: Anta at Aliceepostadressepoeng tilnull. Hva betyr dette? Betyr det at Alice ikke har en e-postadresse? Eller at e-postadressen hennes er ukjent? Eller at det er hemmelig? Eller betyr det rett og slett detepostadresseer 'udefinert' eller 'uinitialisert'? La oss se. Etter å ha lest denne artikkelen, bør alle kunne svare på slike spørsmål uten å nøle.

Merk:Denne artikkelen er programmeringsspråknøytral - så langt det er mulig. Forklaringer er generelle og ikke knyttet til et spesifikt språk. Se bruksanvisningene for programmeringsspråket for spesifikke råd omnull. Imidlertid inneholder denne artikkelen noen enkle kildekodeeksempler vist i Java. Men det er ikke vanskelig å oversette dem til favorittspråket ditt.

Run-time Implementering

Før vi diskuterer betydningen avnull, vi må forstå hvordannullimplementeres i minnet under kjøring.

Merk:Vi skal ta en titt på entypiskImplementering avnull. Den faktiske implementeringen i et gitt miljø avhenger av programmeringsspråket og målmiljøet, og kan avvike fra implementeringen vist her.

Anta at vi har følgende kildekodeinstruksjon:

Strengnavn = "Bob";

Her erklærer vi en variabel av typenStringog med identifikatorenNavnsom peker på strengen"Bob".

Å si «peker på» er viktig i denne sammenhengen, fordi vi forutsetter at vi jobber medreferansetyper(og ikke medverdityper). Mer om dette senere.

For å gjøre ting enkelt, vil vi gjøre følgende forutsetninger:

  • Instruksjonen ovenfor utføres på en 16-bits CPU med et 16-bits adresserom.
  • Strenger er kodet som UTF-16. De avsluttes med 0 (som i C eller C++).

Følgende bilde viser et utdrag av minnet etter å ha utført instruksjonen ovenfor:

En rask og grundig guide til "null": hva det er, og hvordan du bør bruke det (3)

Minneadressene i bildet ovenfor er valgt vilkårlig og er irrelevante for vår diskusjon.

Som vi kan se, strengen"Bob"er lagret på adresse B000 og opptar 4 minneceller.

VariabelNavnligger på adresse A0A1. Innholdet i A0A1 er B000, som er startminnet til strengen"Bob". Derfor sier vi: VariabelenNavn poeng til "Bob".

Så langt så bra.

Anta nå at du, etter å ha utført instruksjonen ovenfor, utfører følgende:

navn = null;

Navnpoeng tilnull.

Og dette er den nye tilstanden i minnet:

En rask og grundig guide til "null": hva det er, og hvordan du bør bruke det (4)

Vi kan se at ingenting har endret seg for strengen"Bob"som fortsatt er lagret i minnet.

Merk: Minnet som trengs for å lagre strengen"Bob"kan senere bli utgitt hvis det er en søppeloppsamler og ingen andre referanser peker på"Bob", men dette er irrelevant i vår diskusjon.

Det som er viktig er at innholdet i A0A1 (som representerer verdien av variabelNavn) er nå 0000. Så variabelNavnpeker ikke på"Bob"lenger. Verdien 0 (alle biter på null) er en typisk verdi som brukes i minnet for å anginull. Det betyr at det finnesingen verdi knyttet tilNavn. Du kan også tenke på det somfraværet av dataeller rett og slettingen data.

Merk: Den faktiske minneverdien som brukes til å anginuller implementeringsspesifikk. For eksempelJava Virtual Machine-spesifikasjonstår på slutten av avsnittet2.4. "Referansetyper og verdier:"

Java Virtual Machine-spesifikasjonen krever ikke en konkret verdikodingnull.

Huske:

Hvis en referanse peker pånull, det betyr ganske enkelt at det er det ingen verdi knyttet til det.

Teknisk sett inneholder minneplasseringen som er tildelt referansen verdien 0 (alle biter på null), eller en hvilken som helst annen verdi som angirnulli det gitte miljøet.

Opptreden

Som vi lærte i forrige avsnitt, operasjoner som involverernuller ekstremt raske og enkle å utføre under kjøring.

(Video) Hvordan gjøre en grundig jobbanalyse

Det er bare to typer operasjoner:

  • Initialiser eller angi en referanse tilnull(f.eks.navn = null): Det eneste du kan gjøre er å endre innholdet i én minnecelle (f.eks. sette den til 0).
  • Sjekk om en referanse peker pånull(f.eks.hvis navn == null): Det eneste du må gjøre er å sjekke om minnecellen til referansen har verdien 0.

Huske:

Operasjoner pånuller ekstremt raske og billige.

Referanse vs verdityper

Så langt har vi antatt å jobbe medreferansetyper. Grunnen til dette er enkel:nulleksisterer ikke forverdityper.

Hvorfor?

Som vi har sett tidligere, er en referanse enpekerentil en minneadresse som lagrer en verdi (f.eks. en streng, en dato, en kunde, hva som helst). Hvis en referanse peker pånull, da er ingen verdi knyttet til den.

På den annen side er en verdi per definisjon selve verdien. Det er ingen peker involvert. En verditype lagres som selve verdien. Derfor konseptet mednullfinnes ikke for verdityper.

Følgende bilde viser forskjellen. På venstre side kan du se igjen minnet ved variabelNavnvære en referanse som peker på "Bob". Høyre side viser minnet i tilfelle variabelNavnvære en verditype.

En rask og grundig guide til "null": hva det er, og hvordan du bør bruke det (5)

Som vi kan se, i tilfelle av en verditype, lagres selve verdien direkte på adressen A0A1 som er assosiert med variabelNavn.

Det vil være mye mer å si om referanse kontra verdityper, men dette er utenfor rammen av denne artikkelen. Vær også oppmerksom på at noen programmeringsspråk kun støtter referansetyper, andre støtter kun verdityper, og noen (f.eks. C# og Java) støtter begge.

Huske:

Konseptet avnulleksisterer kun forhenvisningtyper. Det finnes ikke forverdityper.

Betydning

Anta at vi har en typepersonmed et feltepostadresse. Anta også at for en gitt person som vi vil kalle Alice,epostadressepoeng tilnull.

Hva betyr dette? Betyr det at Alice ikke har en e-postadresse? Ikke nødvendigvis.

Som vi allerede har sett, er det vi kan hevdeingen verdi er knyttet til e-postadresse.

MenHvorforer det ingen verdi? Hva er grunnen tilepostadressepeker pånull? Hvis vi ikke kjenner konteksten og historien, kan vi bare spekulere. Grunnen tilnull kunnevære:

Alice har ingen e-postadresse. Eller…

Alice har en e-postadresse, men:

  • den er ennå ikke lagt inn i databasen
  • det er hemmelig (uavslørt av sikkerhetsgrunner)
  • det er en feil i en rutine som lager et personobjekt uten innstillingsfeltepostadresse
  • og så videre.

I praksis kjenner vi ofte bruken og konteksten. Vi knytter intuitivt en presis mening tilnull. I en enkel og feilfri verden,nullville ganske enkelt bety at Alice faktisk ikke har en e-postadresse.

Når vi skriver kode, er årsakenHvorforen referanse peker pånuller ofte irrelevant. Vi bare sjekker etternullog ta passende tiltak. Anta for eksempel at vi må skrive en løkke som sender e-post for en liste over personer. Koden (i Java) kan se slik ut:

for ( Person person: personer ) { if ( person.getEmailAddress() != null ) { // kode for å sende e-post } else { logger.warning("Ingen e-postadresse for " + person.getName()); }}

I løkken ovenfor bryr vi oss ikke om årsaken tilnull. Vi bare erkjenner det faktum at det ikke er noen e-postadresse, logger en advarsel og fortsetter.

Huske:

Hvis en referanse peker pånullda betyr det alltid at det er det ingen verdi knyttet til det.

I de fleste tilfeller,nullhar enmer spesifikk betydning som avhenger av konteksten.

Hvorfor er detnull?

Noen ganger deterviktig å viteHvorforen referanse peker pånull.

Tenk på følgende funksjonssignatur i en medisinsk applikasjon:

Liste getAllergiesOfPatient ( String patientId )

I dette tilfellet tilbakenull(eller en tom liste) er tvetydig. Betyr det at pasienten ikke har allergi, eller betyr det at en allergitest ennå ikke er utført? Dette er to semantisk svært forskjellige saker som må håndteres forskjellig. Ellers kan utfallet være livstruende.

Bare anta at pasienten har allergier, men en allergitest er ennå ikke utført og programvaren forteller legen at "det er ingen allergier". Derfor trenger vi tilleggsinformasjon. Vi trenger å viteHvorforfunksjonen returnerernull.

Det ville være fristende å si: Vel, for å skille, kommer vi tilbakenulldersom det ennå ikke er utført en allergitest, og vi returnerer en tom liste dersom det ikke er allergier.

IKKE GJØR DETTE!

(Video) Webinar - hvordan gjøre grundig jobbanalyse og skrive gode utlysningstekster?

Dette er dårlig datadesign av flere grunner.

De forskjellige semantikkene for returnullversus å returnere en tom liste må være godt dokumentert. Og som vi alle vet, kan kommentarer være feil (dvs. inkonsistente med koden), utdaterte, eller de kan til og med være utilgjengelige.

Det er ingen beskyttelse for misbruk i klientkode som kaller opp funksjonen. For eksempel er følgende kode feil, men den kompileres uten feil. Dessuten er feilen vanskelig å få øye på for en menneskelig leser. Vi kan ikke se feilen bare ved å se på koden uten å vurdere kommentaren tilgetAllergiesOfPatient:

List allergies = getAllergiesOfPatient ( "123" );if ( allergier == null ) { System.out.println ( "Ingen allergier" ); // <-- FEIL!} else if ( allergies.isEmpty() ) { System.out.println ( "Test ikke utført ennå" ); // <-- FEIL!} else { System.out.println ( "Det er allergier" );}

Følgende kode ville også være feil:

List allergies = getAllergiesOfPatient ( "123" );if ( allergier == null || allergies.isEmpty() ) { System.out.println ( "Ingen allergier" ); // <-- FEIL!} else { System.out.println ( "Det er allergier" );}

Hvisnull/tom-logikk avgetAllergiesOfPatientendringer i fremtiden, så må kommentaren oppdateres, samt all klientkode. Og det er ingen beskyttelse mot å glemme noen av disse endringene.

Hvis det senere er et annet tilfelle som skal skilles ut (f.eks. venter en allergitest - resultatene er ikke tilgjengelige ennå), eller hvis vi ønsker å legge til spesifikke data for hvert tilfelle, så står vi fast.

Så funksjonen må returnere mer informasjon enn bare en liste.

Det er forskjellige måter å gjøre dette på, avhengig av hvilket programmeringsspråk vi bruker. La oss ta en titt på enmuligløsning i Java.

For å differensiere sakene definerer vi en overordnet typeAllergitestresultat, samt tre undertyper som representerer de tre tilfellene (Ikke ferdig,Avventer, ogFerdig):

grensesnitt AllergyTestResult {}
grensesnitt NotDoneAllergyTestResult utvider AllergyTestResult {}
grensesnitt PendingAllergyTestResult utvider AllergyTestResult { public Date getDateStarted();}
interface DoneAllergyTestResult extends AllergyTestResult { public Date getDateDone(); offentlig liste getAllergies(); // null hvis ingen allergier // ikke-tom hvis det er // allergier}

Som vi kan se, kan vi for hvert enkelt tilfelle ha spesifikke data knyttet til det.

I stedet for bare å returnere en liste,getAllergiesOfPatientreturnerer nå enAllergitestresultatgjenstand:

AllergyTestResult getAllergiesOfPatient( String patientId )

Klientkoden er nå mindre utsatt for feil og ser slik ut:

AllergyTestResult allergyTestResult = getAllergiesOfPatient("123");
if (allergyTestResult-forekomst av NotDoneAllergyTestResult) { System.out.println ( "Test ikke utført ennå" ); } else if (allergyTestResult-forekomst av PendingAllergyTestResult) { System.out.println ("Test venter"); } else if (allergyTestResult-forekomst av DoneAllergyTestResult) { List list = ((DoneAllergyTestResult) allergyTestResult).getAllergies(); if (liste == null) { System.out.println ( "Ingen allergier" ); } else if (list.isEmpty()) { assert false; } else { System.out.println ( "Det er allergier" ); }} annet { påstå falsk;}

Merk: Hvis du synes at koden ovenfor er ganske detaljert og litt vanskelig å skrive, så er du ikke alene. Noen moderne språk lar oss skrive konseptuelt lik kode mye mer kortfattet. Og nullsikre språk skiller mellom nullbare og ikke-nullbare verdier på en pålitelig måte ved kompilering - det er ikke nødvendig å kommentere nullbarheten til en referanse eller å sjekke om en referanse som er erklært å være ikke-null ved et uhell har blitt satt tilnull.

Huske:

Hvis vi trenger å vite hvorfor det ikke er noen verdi knyttet til en referanse, datilleggsdata må gis for å skille de mulige tilfellene.

Initialisering

Vurder følgende instruksjoner:

String s1 = "foo";String s2 = null;String s3;

Den første instruksen erklærer enStringvariabels1og tildeler den verdien"foo".

Den andre instruksjonen tildelernulltils2.

Den mer interessante instruksjonen er den siste. Ingen verdi er eksplisitt tildelts3. Derfor er det rimelig å spørre: Hva er tilstanden tils3etter dens erklæring? Hva vil skje hvis vi skrivers3til OS-utgangsenheten?

Det viser seg at tilstanden til en variabel (eller klassefelt) deklarert uten å tildele en verdi, avhenger av programmeringsspråket. Dessuten kan hvert programmeringsspråk ha spesifikke regler for forskjellige tilfeller. For eksempel gjelder forskjellige regler for referansetyper og verdityper, statiske og ikke-statiske medlemmer av en klasse, globale og lokale variabler og så videre.

Så vidt jeg vet, er følgende regler typiske variasjoner som oppstår:

  • Det er ulovlig å deklarere en variabel uten også å tildele en verdi
  • Det er en vilkårlig verdi lagret is3, avhengig av minneinnholdet på kjøringstidspunktet - det er ingen standardverdi
  • En standardverdi blir automatisk tilordnets3.For en referansetype er standardverdiennull.Når det gjelder en verditype, avhenger standardverdien av variabelens type. For eksempel0for heltall,falskfor en boolsk og så videre.
  • staten avs3er 'udefinert'
  • staten avs3er 'uinitialisert', og ethvert forsøk på å brukes3resulterer i en kompileringsfeil.

Det beste alternativet er det siste. Alle andre alternativer er utsatt for feil og/eller upraktiske - av grunner vi ikke vil diskutere her, fordi denne artikkelen fokuserer pånull.

Som et eksempel bruker Java det siste alternativet for lokale variabler. Følgende kode resulterer derfor i en kompileringstidsfeil på den andre linjen:

String s3;System.out.println ( s3 );

Kompilatorutgang:

feil: variabel s3 er kanskje ikke initialisert

Huske:

Hvis en variabel er deklarert, men ingen eksplisitt verdi er tilordnet den,da avhenger tilstanden av flere faktorer som er forskjellige på forskjellige programmeringsspråk.

På noen språk,nuller standardverdien for referansetyper.

Når du skal brukenull(Og når du ikke skal bruke det)

Grunnregelen er enkel:nullbør bare tillates når det er fornuftig at en objektreferanse har 'ingen verdi knyttet til seg'. (Merk: en objektreferanse kan være en variabel, konstant, egenskap (klassefelt), input/output argument, og så videre.)

Anta for eksempel typepersonmed feltNavnogdateOfFirstMarriage:

grensesnitt Person { public String getName(); offentlig Date getDateOfFirstMarriage();}

Hver person har et navn. Derfor gir det ikke mening for feltNavnå ha 'ingen verdi knyttet til det'. FeltNavnerikke nullbar. Det er ulovlig å tildelenulltil det.

(Video) Lynkurs i Python-programmering

På den annen side feltdateOfFirstMarriagerepresenterer ikke en påkrevd verdi. Ikke alle er gift. Derfor gir det mening fordateOfFirstMarriageå ha 'ingen verdi knyttet til det'. DerfordateOfFirstMarriageer ennullbarfelt. Hvis en personsdateOfFirstMarriagefeltet peker pånullda betyr det ganske enkelt at denne personen aldri har vært gift.

Merk: Dessverre skiller de fleste populære programmeringsspråk ikke mellom nullbare og ikke-nullbare typer. Det er ingen måte å si det pålitelig pånullkan aldri tilordnes en gitt objektreferanse. På noen språk er det mulig å bruke merknader, for eksempel de ikke-standardiserte merknadene @Nullable og @NonNullable i Java. Her er et eksempel:

grensesnitt Person { public @Nonnull String getName(); offentlig @Nullable Date getDateOfFirstMarriage();}

Imidlertid brukes ikke slike merknader av kompilatoren for å sikre null-sikkerhet. Likevel er de nyttige for den menneskelige leseren, og de kan brukes av IDE-er og verktøy som statiske kodeanalysatorer.

Det er viktig å merke seg detnullskal ikke brukes til å angi feiltilstander.

Tenk på en funksjon som leser konfigurasjonsdata fra en fil. Hvis filen ikke eksisterer eller er tom, bør en standardkonfigurasjon returneres. Her er funksjonens signatur:

public Config readConfigFromFile (filfil)

Hva skal skje i tilfelle en fillesefeil?

Bare returnernull?

NEI!

Hvert språk har sin egen standardmåte for å signalisere feiltilstander og gi data om feilen, for eksempel en beskrivelse, type, stabelsporing og så videre. Mange språk (C#, Java osv.) bruker en unntaksmekanisme, og unntak bør brukes i disse språkene for å signalisere kjøretidsfeil.readConfigFromFileskal ikke returnerenullfor å angi en feil. I stedet bør funksjonens signatur endres for å gjøre det klart at funksjonen kan mislykkes:

public Config readConfigFromFile ( File file ) kaster IOException

Huske:

Tillatenullbare hvis det er fornuftig at en objektreferanse har 'ingen verdi knyttet til seg'.

Ikke bruknullfor å signalisere feiltilstander.

Null-sikkerhet

Tenk på følgende kode:

Strengnavn = null;int l = navn.lengde();

Ved kjøretid resulterer koden ovenfor i den beryktedenull-pekerfeil, fordi vi prøver å utføre en metode for en referanse som peker pånull. I C#, for eksempel, aNullReferenceExceptioner kastet, i Java er det enNullPointerException.

Nullpekerfeilen er ekkel.

Det er denhyppigste feili mange programvareapplikasjoner, og har vært årsaken til utallige problemer i programvareutviklingens historie. Tony Hoare, oppfinneren avnull, kaller det 'milliardfeilen'.

Men Tony Hoare (Turing Award-vinner i 1980 og oppfinner av Quicksort-algoritmen), gir også et hint til en løsning i sintale:

Nyere programmeringsspråk ... har introdusert erklæringer for ikke-nullreferanser. Dette er løsningen, som jeg avviste i 1965.

I motsetning til noen vanlig tro, er den skyldige ikke detnullper se. Problemet ermangel på støtte tilnullhåndteringpå mange programmeringsspråk. For eksempel, i skrivende stund (mai 2018), er ingen av de ti beste språkene iTiobe indeksskiller naturlig mellom nullbare og ikke-nullbare typer.

Derfor gir noen nye språk kompileringstid null-sikkerhet og spesifikk syntaks for praktisk håndteringnulli kildekoden. På disse språkene vil koden ovenfor resultere i en kompileringsfeil. Programvarekvalitet og pålitelighet øker betraktelig, fordi null-pekerfeilen forsvinner.

Null-sikkerhet er et fascinerende emne som fortjener en egen artikkel.

Huske:

Når det er mulig, bruk et språk somstøtter null-sikkerhet ved kompilering.

Merk: Noen programmeringsspråk (for det meste funksjonelle programmeringsspråk som Haskell) støtter ikke konseptetnull. I stedet bruker deKanskje/valgfritt mønster å representere "fraværet av en verdi". Kompilatoren sikrer at "ingen verdi"-saken blir eksplisitt behandlet. Derfor kan null-pekerfeil ikke oppstå.

Sammendrag

Her er et sammendrag av viktige punkter å huske:

  • Hvis en referanse peker pånull, det betyr alltid at det er detingen verdi knyttet til det.
  • I de fleste tilfeller,nullhar en mer spesifikk betydning som avhenger av konteksten.
  • Hvis vi trenger å viteHvorfordet er ingen verdi knyttet til en referanse, da må tilleggsdata gis for å skille de mulige tilfellene.
  • Tillatenullbare hvis det er fornuftig at en objektreferanse har 'ingen verdi knyttet til seg'.
  • Ikke bruknullfor å signalisere feiltilstander.
  • Konseptet avnulleksisterer kun for referansetyper. Det finnes ikke for verdityper.
  • På noen språknuller standardverdien for referansetyper.
  • nulloperasjonene er ekstremt raske og billige.
  • Når det er mulig, bruk et språk som støtter compile-time-null-safety.

ANNONSE

ANNONSE

ANNONSE

(Video) Maling av kjøkkenfronter [Viser hvordan du får et strøkent resultat med fordriver]

ANNONSE

ANNONSE

ANNONSE

ANNONSE

ANNONSE

ANNONSE

ANNONSE

ANNONSE

ANNONSE

ANNONSE

ANNONSE

ANNONSE

Hvis denne artikkelen var nyttig, .

Lær å kode gratis. freeCodeCamps åpen kildekode-pensum har hjulpet mer enn 40 000 mennesker med å få jobb som utviklere.Kom i gang

ANNONSE

(Video) Bilcamping i iskaldt med hund - Taktelt

Videos

1. Create a Journal to Sell on Amazon KDP for FREE
(Paul Marles)
2. Hvordan tjene penger nok til å investere 500 000 kr på et halvt år? Her er mine inntekter:
(Pengesnakk)
3. Webinar: Hva er grønn anskaffelsespraksis?
(Direktoratet for forvaltning og økonomistyring)
4. What Happens If You Don't Eat For 5 Days?
(Dr. Sten Ekberg)
5. Acer Aspire 5 (2019) Keyboard replacement and Teardown
(TechsavvyProductions)
6. Digitale anskaffelser – mer enn EHF
(Direktoratet for forvaltning og økonomistyring)

References

Top Articles
Latest Posts
Article information

Author: Duane Harber

Last Updated: 10/15/2023

Views: 5367

Rating: 4 / 5 (71 voted)

Reviews: 86% of readers found this page helpful

Author information

Name: Duane Harber

Birthday: 1999-10-17

Address: Apt. 404 9899 Magnolia Roads, Port Royceville, ID 78186

Phone: +186911129794335

Job: Human Hospitality Planner

Hobby: Listening to music, Orienteering, Knapping, Dance, Mountain biking, Fishing, Pottery

Introduction: My name is Duane Harber, I am a modern, clever, handsome, fair, agreeable, inexpensive, beautiful person who loves writing and wants to share my knowledge and understanding with you.