Oinitierad variabel
Vid beräkning är en oinitierad variabel en variabel som deklareras men som inte sätts till ett definitivt känt värde innan den används. Det kommer att ha ett visst värde, men inte ett förutsägbart sådant. Som sådan är det ett programmeringsfel och en vanlig källa till buggar i programvara.
Exempel på C-språket
Ett vanligt antagande från nybörjare är att alla variabler sätts till ett känt värde, såsom noll, när de deklareras. Även om detta är sant för många språk, är det inte sant för alla, och därför finns risken för fel. Språk som C använder stackutrymme för variabler, och samlingen av variabler som allokeras för en subrutin är känd som en stackframe . Även om datorn kommer att avsätta lämplig mängd utrymme för stackramen, gör den det vanligtvis helt enkelt genom att justera värdet på stackpekaren och ställer inte själva minnet till något nytt tillstånd (vanligtvis av effektivitetsskäl). Allt innehåll i det minnet vid den tidpunkten kommer därför att visas som initiala värden för de variabler som upptar dessa adresser.
Här är ett enkelt exempel i C:
0
void count ( void ) { int k , i ; för ( i = ; i < 10 ; i ++ ) { k = k + 1 ; } printf ( "%d" , k ); }
Slutvärdet på k
är odefinierat. Svaret att det måste vara 10 förutsätter att det började på noll, vilket kan vara sant eller inte. Observera att i exemplet initieras variabeln i till noll av den första satsen i
for-
satsen.
Ett annat exempel kan vara när man har att göra med strukturer . I kodavsnittet nedan har vi en structstudent
som innehåller några variabler som beskriver informationen om en elev. Funktionen register_student
läcker minnesinnehåll eftersom den misslyckas med att fullständigt initiera medlemmarna i struct student new_student
. Om vi tittar närmare så initieras i början ålder
, termin
och student_number .
Men initialiseringen av first_name
och last_name
medlemmarna är felaktig. Detta beror på att om längden på teckenuppsättningarna förnamn
och efternamn är mindre än 16 byte, under
strcpy
, misslyckas vi med att fullständigt initiera hela 16 byte av minne som är reserverat för var och en av dessa medlemmar. Därför läcker vi lite stackminne till den som ringer efter att memcpy()
har angett den resulterande strukturen till utmatning .
0
struct student { unsigned int age ; osignerad int termin ; char förnamn [ 16 ]; char efternamn [ 16 ]; osignerad int student_number ; }; int register_student ( struct student * output , int age , char * first_name , char * last_name ) { // Om någon av dessa pekare är Null, misslyckas vi. if ( ! output || ! first_name || ! last_name ) { printf ( "Fel! \n " ); returnera -1 ; } // Vi ser till att strängarnas längd är mindre än 16 byte (inklusive null-byte) // för att undvika överflöden if ( strlen ( first_name ) > 15 || strlen ( last_name ) > 15 ) { printf ( "förnamn och efternamn får inte vara längre än 16 tecken! \n " ); returnera -1 ; } // Initiering av medlemmarna struct student new_student ; ny_student . ålder = ålder ; ny_student . termin = 1 ; ny_student . student_number = get_new_student_number (); strcpy ( ny_student . förnamn , förnamn ); strcpy ( ny_student . efternamn , efternamn ); //kopiera resultatet till utdata memcpy ( output , & new_student , sizeof ( struct student )); återvända ; }
0 I alla fall, även när en variabel implicit initieras till ett standardvärde som 0, är detta vanligtvis inte det korrekta värdet. Initialiserad betyder inte korrekt om värdet är ett standardvärde. (Men standardinitiering till är en rätt praxis för pekare och matriser av pekare, eftersom det gör dem ogiltiga innan de faktiskt initieras till sitt korrekta värde.) I C initieras variabler med statisk lagringsvaraktighet som inte initieras explicit till noll (eller null, för pekare).
Inte bara är oinitierade variabler en vanlig orsak till buggar, utan den här typen av buggar är särskilt allvarliga eftersom de kanske inte är reproducerbara: till exempel kan en variabel förbli oinitierad endast i någon gren av programmet. I vissa fall kan program med oinitierade variabler till och med klara programvarutest .
Effekter
Oinitierade variabler är kraftfulla buggar eftersom de kan utnyttjas för att läcka godtyckligt minne eller för att uppnå godtycklig minnesöverskrivning eller för att få kodexekvering, beroende på fallet. När man utnyttjar en programvara som använder randomisering av adressutrymmeslayout (ASLR), krävs det ofta att man känner till basadressen för programvaran i minnet. Att utnyttja en oinitierad variabel på ett sätt för att tvinga programvaran att läcka en pekare från dess adressutrymme kan användas för att kringgå ASLR.
Använd i språk
Oinitierade variabler är ett särskilt problem i språk som assemblerspråk, C och C++ , som designades för systemprogrammering . Utvecklingen av dessa språk involverade en designfilosofi där konflikter mellan prestanda och säkerhet i allmänhet löstes till förmån för prestanda. Programmeraren fick bördan att vara medveten om farliga problem som oinitierade variabler.
På andra språk initieras variabler ofta till kända värden när de skapas. Exempel inkluderar:
- VHDL initierar alla standardvariabler till ett speciellt 'U'-värde. Den används i simulering, för felsökning, för att låta användaren veta när de som inte bryr sig , genom flervärdslogiken , påverkar utdata.
- Java har inga oinitierade variabler. Fält med klasser och objekt som inte har en explicit initialiserare och element i arrayer initieras automatiskt med standardvärdet för deras typ (falskt för booleskt, 0 för alla numeriska typer, null för alla referenstyper). Lokala variabler i Java måste definitivt tilldelas innan de nås, annars är det ett kompileringsfel.
-
Python initierar lokala variabler till
NULL
(till skillnad frånNone
) och höjer ettUnboundLocalError
när en sådan variabel nås innan den (om)initieras till ett giltigt värde. - D initierar alla variabler om de inte uttryckligen anges av programmeraren att inte göra det.
, kommer många kompilatorer att försöka identifiera användningen av oinitierade variabler och rapportera dem som kompileringsfel . Vissa språk hjälper till med denna uppgift genom att erbjuda konstruktioner för att hantera initieringen av variabler; till exempel C# en speciell smak av call-by-referens-parametrar till subrutiner (specificerade som out
istället för den vanliga ref
), som hävdar att variabeln tillåts vara oinitierad vid inträde men kommer att initieras efteråt.