Returnera uttalande
I datorprogrammering gör en retursats att exekvering lämnar den aktuella subrutinen och återupptas vid punkten i koden omedelbart efter instruktionen som anropade subrutinen, känd som dess returadress . Returadressen sparas av anropsrutinen, idag oftast på processens anropsstack eller i ett register . Retursatser i många programmeringsspråk tillåter en funktion att specificera ett returvärde som ska skickas tillbaka till koden som anropade funktionen.
Översikt
I C och C++ returnerar du exp
; (där exp
är ett uttryck ) är en sats som säger åt en funktion att återställa körningen av programmet till den anropande funktionen och rapportera värdet på exp
. Om en funktion har returtypen void , kan returnsatsen användas utan värde, i vilket fall programmet bara bryter sig ur den aktuella funktionen och återgår till den anropande.
I Pascal finns inget returmeddelande. (Men i nyare Pascals Exit( exp );
användas för att returnera ett värde omedelbart. Utan parametrar bryter den bara ut proceduren.) En subrutin återkommer automatiskt när exekveringen når sin sista körbara sats. Värden kan returneras genom att tilldela en identifierare som har samma namn som subrutinen, en funktion i Pascal-terminologi. På så sätt används funktionsidentifieraren för rekursiva anrop och som resultathållare; detta liknar syntaktisk en explicit utdataparameter . Samma syntax används i Fortran 66 och Fortran 77 även om en retursats lades till i FORTRAN II . På vissa andra språk används en användardefinierad resultatvariabel istället för funktionsidentifieraren.
Oberon ( Oberon-07 ) har en returklausul istället för en returuppgift. Returklausulen placeras efter förfarandeorganets sista uttalande. Detta möjliggör kompileringskontroll av korrekt retur- och returvärde från proceduren.
Vissa uttrycksorienterade programmeringsspråk , som Lisp , Perl och Ruby , tillåter programmeraren att utelämna en explicit retursats och istället specificera att det senast utvärderade uttrycket är subrutinens returvärde.
I andra fall returneras ett Null-värde om det inte finns någon explicit retursats: i Python returneras värdet None
när retursatsen utelämnas, medan i JavaScript returneras värdet undefined .
I Windows PowerShell returneras alla utvärderade uttryck som inte fångas (t.ex. tilldelade till en variabel, cast to void eller piped till $null ) från subrutinen som element i en array, eller som ett enda objekt om endast ett objekt har inte fångats.
I Perl kan ett returvärde eller värden för en subrutin bero på i vilket sammanhang den anropades. Den mest grundläggande distinktionen är ett skalärt sammanhang där den anropande koden förväntar sig ett värde, en listkontext där den anropande koden förväntar sig en lista med värden och en void kontext där den anropande koden inte förväntar sig något returvärde alls. En subrutin kan kontrollera sammanhanget med hjälp av wantarray
-funktionen. En speciell syntax för retur utan argument används för att returnera ett odefinierat värde i skalär kontext och en tom lista i listkontext. Den skalära kontexten kan delas in ytterligare i booleska , nummer, sträng och olika referenstyper . Ett sammanhangskänsligt objekt kan också returneras med en kontextuell retursekvens, med lat utvärdering av skalära värden.
Många operativsystem låter ett program returnera ett resultat (separat från normal utdata ) när dess process avslutas; dessa värden kallas returkoder eller mer specifikt utgångsstatus . Mängden information som kan förmedlas på detta sätt är ganska begränsad, i praktiken ofta begränsad till att signalera framgång eller misslyckande. Inifrån programmet uppnås denna retur vanligtvis genom att anropa Exit (systemanrop) (vanlig även i C, där den alternativa mekanismen för att återvända från huvudfunktionen är tillgänglig).
Syntax
Återvändande uttalanden finns i många former. Följande syntaxer är vanligast:
Språk | Returutlåtande | Om värdet utelämnas, returnera |
---|---|---|
Ada , Bourne shell , C , C++ , Java , PHP , C# , JavaScript , D |
returvärde ; _
|
i Bourne-skalet, exit-värdet för det senaste kommandot som kördes i funktionen i C och C++, odefinierat beteende om funktionen är värdereturnerande i PHP, returnerar i Javascript, returnerar värdet i Java och C#, inte tillåtet om funktionen är värdereturnerande |
GRUNDLÄGGANDE |
LÄMNA TILLBAKA
|
|
Läspa |
( returvärde ) _
|
sista utsagans värde |
Perl , Ruby |
returnera @värden ; returnera $värde ; återvända ;
eller en kontextuell retursekvens |
sista utsagans värde |
PL/I |
return(uttryck); lämna tillbaka; |
odefinierat beteende om proceduren deklareras som att returnera ett värde |
Pytonorm |
returvärde _
|
Ingen
|
Småprat |
^ värde
|
|
Tcl |
return return $value return - kodfel " Felmeddelande"
eller någon mer komplicerad kombination av alternativ |
sista utsagans värde |
Visual Basic .NET |
Returvärde _
|
|
Windows PowerShell |
returvärde ; _
|
objekt |
x86 montering |
röta
|
innehållet i eax-registret (enligt konventioner) |
I vissa assemblerspråk , till exempel det för MOS Technology 6502, används mnemoniska "RTS" (Return from Subroutine).
Flera returer
Språk med en explicit retursats skapar möjligheten till flera retursatser i samma funktion. Huruvida det är bra eller inte är kontroversiellt.
Starka anhängare av strukturerad programmering ser till att varje funktion har en enda ingång och en enda utgång (SESE). Det har därför hävdats att man bör undvika användningen av den explicita retursatsen utom i textänden av en subrutin, med tanke på att den, när den används för att "återvända tidigt", kan lida av samma sorts problem som uppstår för GOTO uttalandet . Omvänt kan det hävdas att det är värt besväret att använda return-satsen när alternativet är mer invecklad kod, såsom djupare kapsling, vilket skadar läsbarheten.
I sin lärobok från 2004 skriver David Watt att "single-entry multi-exit kontrollflöden är ofta önskvärda". Med hjälp av Tennents ramverksuppfattning om sequencer beskriver Watt enhetligt kontrollflödeskonstruktionerna som finns i samtida programmeringsspråk och försöker förklara varför vissa typer av sequencers är att föredra framför andra i samband med multi-exit kontrollflöden. Watt skriver att obegränsade gotos (hoppsekvenserare) är dåliga eftersom målet för hoppet inte är självförklarande för läsaren av ett program förrän läsaren hittar och undersöker själva etiketten eller adressen som är målet för hoppet. Däremot hävdar Watt att den konceptuella avsikten med en retursequencer är tydlig från dess eget sammanhang, utan att behöva undersöka dess destination. Vidare skriver Watt att en klass av sequencers känd som escape-sequencers , definierade som "sequencer that terminates exekvering av ett textuellt omslutande kommando eller procedur", omfattar både breaks from loops (inklusive multi-level breaks) och return-satser. Watt noterar också att även om hoppsekvenserare (gotos) har varit något begränsade i språk som C, där målet måste vara ett inuti det lokala blocket eller ett omslutande yttre block, är den begränsningen ensam inte tillräcklig för att göra avsikten med gotos i C själv -beskriva och så att de fortfarande kan producera " spagettikod ". Watt undersöker också hur exception-sequencers skiljer sig från escape- och jump-sequencers; för detaljer om detta, se artikeln om strukturerad programmering .
Enligt empiriska studier som citeras av Eric S. Roberts , hade studentprogrammerare svårt att formulera korrekta lösningar för flera enkla problem på ett språk som Pascal , som inte tillåter flera utgångspunkter. För problemet med att skriva en funktion för att linjärt söka efter ett element i en array, fann en studie från 1980 av Henry Shapiro (citerad av Roberts) att endast 20 % av försökspersonerna använde de Pascal-försedda kontrollstrukturerna. , medan inget ämne skrev felaktig kod för detta problem om det fick skriva en retur från mitten av en loop.
Andra, inklusive Kent Beck och Martin Fowler hävdar att en eller flera skyddsklausuler - villkorliga "early exit"-retursatser nära början av en funktion - ofta gör en funktion lättare att läsa än alternativet.
Det vanligaste problemet vid tidig exit är att rensning eller slutsatser inte exekveras - till exempel är tilldelat minne inte oallokerat eller öppna filer stängs inte, vilket orsakar läckor. Dessa måste göras vid varje returplats, vilket är skört och lätt kan resultera i buggar. Till exempel, i senare utveckling, kan en retursats förbises av en utvecklare, och en åtgärd som bör utföras i slutet av en subrutin (t.ex. en spårsats ) kanske inte utförs i alla fall. Språk utan retursats, som standard Pascal , har inte detta problem. Vissa språk, som C++ och Python, använder koncept som gör att åtgärder kan utföras automatiskt vid retur (eller undantagskast) vilket mildrar några av dessa problem – dessa kallas ofta "försök/äntligen" eller liknande. Funktionalitet som dessa "äntligen"-satser kan implementeras av en goto till subrutinens enda returpunkt. En alternativ lösning är att använda den normala stackavvecklingen (variabel deallokering) vid funktionsutgång för att avallokera resurser, såsom via destruktörer på lokala variabler, eller liknande mekanismer såsom Pythons "with"-sats.
Vissa tidiga implementeringar av språk som den ursprungliga Pascal och C begränsade de typer som kan returneras av en funktion (t.ex. stöder inte post- eller struct -typer) för att förenkla deras kompilatorer .
I Java – och liknande språk modellerade efter det, som JavaScript – är det möjligt att exekvera kod även efter retursatsen, eftersom finalblocket i en try-catch-struktur alltid körs. Så om return -satsen placeras någonstans inom try- eller catch -blocken kommer koden inom slutligen (om den läggs till) att exekveras. Det är till och med möjligt att ändra returvärdet för en icke primitiv typ (en egenskap hos ett redan returnerat objekt) eftersom utgången också sker efteråt.
Avkastningsdeklarationer
Kusin att returnera uttalanden är avkastningssatser : där en retur får en underrutin att avslutas, får en avkastning att en co - rutin avbryts . Coroutinen kommer senare att fortsätta där den avbröts om den ropas upp igen. Coroutiner är betydligt mer involverade att implementera än subrutiner, och därför är avkastningssatser mindre vanliga än retursatser, men de finns på ett antal språk.
Samtals-/retursekvenser
Ett antal möjliga samtals-/retursekvenser är möjliga beroende på hårdvaruinstruktionsuppsättningen, inklusive följande:
- CALL
-
instruktionen skickar adressen till nästa instruktion på stacken och förgrenar sig till den specificerade adressen. RETURN-
instruktionen poppar returadressen från stacken till instruktionspekaren och exekveringen återupptas på den adressen. (Exempel: x86 , PDP-11 ) - CALL
-
instruktionen placerar adressen för nästa instruktion i ett register och förgrenar sig till den specificerade adressen. RETURN-
instruktionssekvensen placerar returadressen från registret i instruktionspekaren och exekveringen återupptas på den adressen. (Exempel: IBM System/360 och efterföljare genom z/Architecture , de flesta RISC- arkitekturer) - CALL-
instruktionen
placerar adressen för nästa (eller aktuella ) instruktion i lagringsplatsen vid anropsadressen och förgrenar sig till den specificerade adressen+1. RETURN-
instruktionssekvensen förgrenar sig till returadressen genom ett indirekt hopp till den första instruktionen i subrutinen. (Exempel: IBM 1130 , SDS 9XX , PDP-8 )