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ån None ) och höjer ett UnboundLocalError 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.

Se även

Vidare läsning