Odefinierat värde

Vid beräkning (särskilt i programmering ) är odefinierat värde ett tillstånd där ett uttryck inte har ett korrekt värde , även om det är syntaktiskt korrekt. Ett odefinierat värde får inte förväxlas med tom sträng , booleskt "falskt" eller andra "tomma" (men definierade) värden. Beroende på omständigheterna kan utvärdering till ett odefinierat värde leda till undantag eller odefinierat beteende , men i vissa programmeringsspråk kan odefinierade värden förekomma under ett normalt, förutsägbart förlopp av programkörning .

Dynamiskt typade språk behandlar vanligtvis odefinierade värden explicit när det är möjligt. Till exempel har Perl undef -operator som kan "tilldela" ett sådant värde till en variabel. I andra typer av system kan ett odefinierat värde betyda ett okänt, oförutsägbart värde, eller bara ett programfel vid försök att utvärdera det. Nullbara typer erbjuder ett mellanliggande tillvägagångssätt; se nedan .

Hantering

Värdet på en delfunktion är odefinierat när dess argument ligger utanför dess definitionsdomän . Detta inkluderar många aritmetiska fall som division med noll , kvadratrot eller logaritm av ett negativt tal etc. Ett annat vanligt exempel är att få tillgång till en array med ett index som är utanför gränserna, liksom värdet i en associativ array för en nyckel som den innehåller inte. Det finns olika sätt att hantera dessa situationer i praktiken:

Reserverat värde

I applikationer där odefinierade värden måste hanteras elegant är det vanligt att reservera ett speciellt nollvärde som kan skiljas från normala värden. Detta löser svårigheten genom att skapa ett definierat värde för att representera det tidigare odefinierade fallet. Det finns många exempel på detta:

  • C- standard I/O-biblioteket reserverar det speciella värdet EOF för att indikera att ingen mer ingång är tillgänglig. Funktionen getchar() returnerar nästa tillgängliga indatatecken, eller EOF om det inte finns fler tillgängliga. (ASCII - teckenkoden definierar ett nolltecken för detta ändamål, men standard-I/O-biblioteket vill kunna skicka och ta emot nolltecken, så det definierar ett separat EOF- värde.)
  • IEEE 754 aritmetikstandarden för flyttal definierar ett speciellt " inte ett tal "-värde som returneras när en aritmetisk operation inte har något definierat värde. Exempel är division med noll eller kvadratroten eller logaritmen av ett negativt tal .
  • Structured Query Language har ett speciellt NULL- värde för att indikera saknade data.
  • Perl-språket låter definieringen av ett uttryck kontrolleras via predikatet defined() .
  • Många programmeringsspråk stöder konceptet med en nollpekare som är skild från alla giltiga pekare, och används ofta som en felretur.
  • Vissa språk tillåter att de flesta typer är nullbara, till exempel C# .
  • De flesta Unix- systemanrop returnerar specialvärdet −1 för att indikera fel.

Medan dynamiskt typade språk ofta säkerställer att oinitierade variabler som standard har ett nollvärde, gör statiskt typade värden det ofta inte och skiljer nollvärden (som är väldefinierade) från oinitierade värden (som inte är det).

Undantagshantering

Vissa programmeringsspråk har ett koncept med undantagshantering för att hantera fel på att returnera ett värde. Funktionen returnerar på ett definierat sätt, men den returnerar inget värde, så det finns inget behov av att uppfinna ett speciellt värde för att returnera.

En variant på detta är signalhantering , som görs på operativsystemnivå och inte integrerat i ett programmeringsspråk. Signalhanterare kan försöka vissa former av återställning, som att avsluta en del av en beräkning, men utan så mycket flexibilitet som helt integrerad undantagshantering.

Ej återkommande funktioner

En funktion som aldrig returnerar har ett odefinierat värde eftersom värdet aldrig kan observeras. Sådana funktioner tilldelas formellt bottentypen , som inte har några värden. Exempel delas in i två kategorier:

  • Funktioner som loopar för alltid . Detta kan uppstå medvetet, eller som ett resultat av en sökning efter något som aldrig kommer att hittas. (Till exempel i fallet med misslyckad μ-operator i en partiell rekursiv funktion .)
  • Funktioner som avslutar beräkningen, till exempel utgångssystemet anropar . Inifrån programmet går detta inte att skilja från föregående fall, men det gör skillnad för den som anropar programmet.

Odefinierat beteende

Alla de föregående metoderna för att hantera odefinierade värden kräver att odefinieringen detekteras. Det vill säga, den anropade funktionen bestämmer att den inte kan returnera ett normalt resultat och vidtar några åtgärder för att meddela den som ringer. I andra änden av spektrumet odefinierat beteende skyldigheten på den som ringer att undvika att anropa en funktion med argument utanför dess domän. Det finns ingen gräns för vad som kan hända. I bästa fall en lätt upptäckbar krasch ; i värsta fall ett subtilt fel i en till synes orelaterade beräkning.

(Den formella definitionen av "odefinierat beteende" inkluderar ännu mer extrema möjligheter, inklusive saker som " stoppa och fatta eld " och "få demoner att flyga ut ur näsan".)

Det klassiska exemplet är en dinglande pekare . Det går mycket snabbt att avreferera en giltig pekare , men det kan vara mycket komplicerat att avgöra om en pekare är giltig. Därför försöker datorhårdvara och lågnivåspråk som C inte att validera pekare innan de hänvisas till dem, utan överför ansvaret till programmeraren. Detta ger snabbhet på bekostnad av säkerheten.

Odefinierat värde sensu strikt

Den strikta definitionen av ett odefinierat värde är en ytligt giltig (icke-null) utdata som är meningslös men inte utlöser odefinierat beteende. Om du till exempel skickar ett negativt tal till den snabba inversa kvadratrotsfunktionen kommer det att producera ett tal. Inte ett särskilt användbart nummer, men beräkningen kommer att slutföra och returnera något .

Odefinierade värden förekommer särskilt ofta i hårdvara. Om en tråd inte innehåller användbar information finns den fortfarande och har en viss spänningsnivå. Spänningen bör inte vara onormal (t.ex. inte en skadlig överspänning ), men den speciella logiska nivån är oviktig.

Samma situation uppstår i mjukvara när en databuffert tillhandahålls men inte är helt fylld. Till exempel konverterar funktionen C-bibliotek strftime en tidsstämpel till läsbar form i en medföljande utdatabuffert. Om utgångsbufferten inte är tillräckligt stor för att hålla resultatet returneras ett fel och buffertens innehåll är odefinierat.

I den andra riktningen tar det öppna systemanropet i POSIX tre argument: ett filnamn, några flaggor och ett filläge. Filläget används endast om flaggorna inkluderar O_CREAT . Det är vanligt att använda en tvåargumentform av open , som ger ett odefinierat värde för filläget, när O_CREAT utelämnas.

Ibland är det användbart att arbeta med sådana odefinierade värden på ett begränsat sätt. Den övergripande beräkningen kan fortfarande vara väldefinierad om det odefinierade värdet senare ignoreras.

Som ett exempel på detta tillåter C-språket att konvertera en pekare till ett heltal, även om det numeriska värdet för det heltal är odefinierat. Det kan fortfarande vara användbart för felsökning, för att jämföra två pekare för likhet eller för att skapa en XOR-länkad lista .

Säker hantering av odefinierade värden är viktigt i optimistiska system för samtidighetskontroll , som upptäcker tävlingsförhållanden i efterhand. Till exempel, läsning av en delad variabel som skyddas av seqlock kommer att producera ett odefinierat värde innan det fastställs att ett racetillstånd inträffade. Det kommer då att kassera odefinierade data och försöka igen. Detta ger ett definierat resultat så länge som operationerna som utförs på de odefinierade värdena inte ger ett fullfjädrat odefinierat beteende.

Andra exempel på att odefinierade värden är användbara är slumptalsgeneratorer och hashfunktioner . De specifika värdena som returneras är odefinierade, men de har väldefinierade egenskaper och kan användas utan fel.

Notation

I beräkningsbarhetsteorin betecknas ett uttrycks odefiniering som expr ↑ och definition som expr ↓.

Se även