Tilldelning är en bindningsmekanism i programmering som gör att du dynamiskt kan ändra förhållandet mellan namnen på dataobjekt (vanligtvis variabler ) med deras värden. Strängt taget är att ändra värden en bieffekt av tilldelningsoperationen, och i många moderna programmeringsspråk returnerar själva operationen också ett visst resultat (vanligtvis en kopia av det tilldelade värdet). På den fysiska nivån är resultatet av en tilldelningsoperation att skriva och skriva om minnesceller eller processorregister .
Tilldelning är en av de centrala konstruktionerna i imperativa programmeringsspråk , implementerad effektivt och enkelt på von Neumann-arkitekturen som är grunden för moderna datorer .
I objektorienterade programmeringsspråk är tilldelningens semantik en helt annan. Till exempel, i Kotlin- språket, vid tilldelning, kopieras objektet, och i Rust -språket flyttas objektet (move-semantics) och det gamla paketet blir ogiltigt.
Logisk programmering tar ett annat, algebraiskt tillvägagångssätt. Här finns inget vanligt ("destruktivt") uppdrag. Det finns bara okända som ännu inte har beräknats, och motsvarande identifierare för att beteckna dessa okända. Programmet bestämmer bara deras värden, de själva är konstanta. Naturligtvis, i implementeringen, skriver programmet till minnet, men programmeringsspråk återspeglar inte detta, vilket ger programmeraren möjlighet att arbeta med identifierare med konstanta värden och inte med variabler.
Ren funktionell programmering använder inga variabler och behöver ingen explicit tilldelningssats.
Den allmänna syntaxen för en enkel tilldelning är följande:
<uttryck till vänster> <tilldelningsoperator> <uttryck till höger>"Uttrycket till vänster" ska efter utvärdering leda till platsen för dataobjektet, till målvariabeln, identifieraren för den minnescell till vilken inspelningen ska göras. Sådana referenser kallas "left-values" ( engelska lvalue ). Typiska exempel på ett vänsterhänt värde är ett variabelnamn ( x), en sökväg till en variabel i namnrymden och bibliotek ( Namespace.Library.Object.AnotherObject.Property), en arraysökväg med ett uttryck i stället för indexet ( this.a[i+j*k]), men mer komplexa alternativ ges längre fram i detta artikel.
"Uttrycket till höger" måste på ett eller annat sätt beteckna det värde som ska tilldelas dataobjektet. Även om namnet på samma variabel står till höger som till vänster så tolkas det annorlunda - sådana referenser kallas "högerhandsvärden" ( engelska rvalue ). Språket som används sätter ytterligare begränsningar på uttrycket : sålunda, i statiskt typade språk, måste det ha samma typ som målvariabeln, eller en typ cast till den; på vissa språk (till exempel C eller Python ), kan en annan tilldelningsoperator ( a=b=c) också inkluderas i uttrycket.
Den vanligaste uppdragsoperatören inom programmeringsspråk är =, :=eller ←. Men speciell syntax kanske inte introduceras - till exempel i Tcl :
ställ in <målvariabel> <uttryck>Denna notation motsvarar att anropa en funktion . På samma sätt, i gammaldags COBOL :
MULTIPERERA 2 MED 2 ATT GER FYRA.Valet av uppdragssymbolen är en kontroversiell fråga bland språkdesigners. Det finns en uppfattning om att användningen av en symbol =för uppdrag förvirrar programmerare och väcker också frågan om att välja en symbol för jämförelseoperatören , vilket är svårt att lösa bra .
Således sa Niklaus Wirth [1] :
Ett välkänt dåligt exempel är valet av ett likhetstecken för att beteckna en uppgift, som går tillbaka till Fortran 1957 och fortfarande blint upprepas av en massa språkutvecklare. Denna dåliga idé störtar den urgamla traditionen att använda " = "-tecknet för att beteckna en jämställdhetsjämförelse, ett predikat som utvärderas till " sant " eller " falskt ". Men i Fortran började denna symbol beteckna uppdrag, tvång till jämlikhet. I det här fallet är operanderna i en ojämlik position: den vänstra operanden, variabeln, måste göras lika med den högra operanden, uttrycket. Så x = y betyder inte samma sak som y = x.
Originaltext (engelska)[ visaDölj] Ett ökänt exempel på en dålig idé var valet av likhetstecknet för att beteckna uppdrag. Det går tillbaka till Fortran 1957 och har blint kopierats av arméer av språkdesigners. Varför är det en dålig idé? Eftersom det stör en sekel gammal tradition att låta "=" beteckna en jämförelse för jämlikhet, ett predikat som är antingen sant eller falskt. Men Fortran gjorde det till att betyda uppdrag, upprätthållande av jämlikhet. I det här fallet är operanderna på ojämlik fot: Den vänstra operanden (en variabel) ska göras lika med den högra operanden (ett uttryck). x = y betyder inte samma sak som y = x. [2]Implementeringen av denna position av Wirth kan anses som att i Pascal- språket , som han är författare till, är uppdragsoperatören :=, medan det för jämförelse helt enkelt används =.
Valet av symbolen för likhetsoperatören på språket när det används =som en uppgift avgörs av:
Notering av likhet i C == är en källa till frekventa fel på grund av möjligheten att använda tilldelning i kontrollkonstruktioner, men på andra språk löses problemet genom att införa ytterligare begränsningar.
Till exempel, i språkuttrycket PL/1 :
A = B = Cvariabeln Аtilldelas det booleska värdet för relationsuttrycket В = С. En sådan notation leder till minskad läsbarhet och används sällan.
Långt ifrån alltid "intuitiva" (för programmerare av imperativa språk) sätt att tolka uppdraget är det enda sanna och möjliga.
Utifrån den syntax som används i imperativa språk är det inte alltid möjligt att förstå hur uppgiftssemantiken implementeras om det inte är uttryckligen definierat i språket.
Till exempel, i Forth , innan tilldelning, måste värdet och adressen för en variabel läggas till datastacken, och detta kan göras långt innan den faktiska tilldelningen utförs.
Exempel:
\ Definiera variabeln AAA och tilldela den värdet 10 på nästa rad VARIABEL AAA 10 AAA!Samma sak lite annorlunda:
tio VARIABEL AAA AAA! TvetydighetTänk på ett exempel:
X=2+1Detta kan förstås som att "resultatet av beräkningen 2+1 (dvs. 3) tilldelas en variabel X" eller som "operationen 2+1 är tilldelad en variabel X". Om språket är statiskt skrivet , så finns det ingen tvetydighet, det löses av typen av variabel X("heltal" eller "operation"). I Prolog är skrivning dynamiskt , så det finns två tilldelningsoperationer: is - tilldelning av ett ekvivalent värde och = - tilldelning av ett mönster. I detta fall:
X är 2 + 1, X = 3 X=2+1, X=3Den första sekvensen kommer att kännas igen som sann, den andra - falsk.
TextNär man hanterar objekt av stora storlekar och komplex struktur använder många språk den så kallade " referenssemantiken ". Detta innebär att tilldelning i klassisk mening inte förekommer utan målvariabelns värde anses vara placerat på samma plats som källvariabelns värde. Till exempel ( Python ):
a = [1, 2, 3] b = a a[1] = 1000Efter det kommer det batt ha ett värde [1, 1000, 3] - helt enkelt för att dess värde faktiskt är värdet av a. Antalet referenser till samma dataobjekt kallas dess kardinalitet, och själva objektet dödas (förstörs eller ges till sopsamlaren ) när dess kardinalitet når noll. Programmeringsspråk på lägre nivå (som C ) tillåter programmeraren att uttryckligen kontrollera om pekaremantik eller kopieringsemantik används.
OperationsersättningMånga språk ger möjlighet att ändra innebörden av ett uppdrag, antingen genom egendomsmekanismen eller genom att överbelasta uppdragsoperatören. Substitution kan behövas för att utföra kontroller av giltigheten av det tilldelade värdet eller andra ytterligare operationer. Överbelastning av uppdragsoperatören används ofta för att tillhandahålla en "djup kopia", det vill säga kopiera värden snarare än referenser, som kopieras som standard på många språk.
Sådana mekanismer gör det möjligt att ge bekvämlighet på jobbet, så för en programmerare är det ingen skillnad mellan att använda en inbyggd operatör och en överbelastad. Av samma anledning är problem möjliga, eftersom den överbelastade operatörens handlingar kan vara helt annorlunda än standardoperatörens handlingar, och funktionsanropet är inte uppenbart och kan lätt misstas för en inbyggd operation.
Eftersom tilldelningsoperatören används flitigt, försöker programmeringsspråksutvecklare att utveckla nya konstruktioner för att förenkla skrivningen av typiska operationer (för att lägga till det så kallade " syntaktiska sockret " till språket). Dessutom, i programmeringsspråk på låg nivå, är inkluderingskriteriet ofta förmågan att kompilera till effektiv körbar kod. [3] C- språket är särskilt känt för denna egenskap .
Ett alternativ till den enkla operatorn är möjligheten att tilldela värdet av ett uttryck till flera objekt . Till exempel, i PL/1 , operatören
SUMMA, TOTAL = 0tilldelar samtidigt noll till variablerna SUMoch TOTAL. I Ada är tilldelning också ett påstående, inte ett uttryck, så notationen för flera tilldelningar är:
SUMMA, TOTAL: Heltal := 0;En liknande uppgift i Python har följande syntax:
summa = totalt = 0Till skillnad från PL/1, Ada och Python, där multipla tilldelningar endast betraktas som en stenografi, i C , Lisp och andra, har denna syntax en strikt grund: tilldelningsoperatorn returnerar helt enkelt värdet som tilldelats den (se ovan). Så det sista exemplet är faktiskt:
summa = (totalt = 0)En rad som denna kommer att fungera i C (om du lägger till ett semikolon i slutet), men kommer att orsaka ett fel i Python.
Vissa språk, som Ruby och Python , stöder en utökad tilldelningssyntax som kallas parallell tilldelning:
a , b = 1 , 11Man tror att ett sådant uppdrag utförs samtidigt och parallellt , vilket gör det möjligt att kortfattat implementera, med hjälp av denna konstruktion, operationen att byta ut värdena för två variabler.
Skriva med parallell tilldelning | "Traditionell" tilldelning: kräver ytterligare en variabel och tre operationer | "Ekonomiskt" uppdrag: kräver ingen extra variabel, men innehåller även tre operationer | Ännu mer "ekonomisk" tilldelning: kräver ingen extra variabel, fungerar med bitoperationer |
---|---|---|---|
a, b = b, a | t = a a = b b=t | a = a + b b = a - b a = a - b | a ^= b b ^= a a ^= b |
Det näst sista aritmetiska alternativet är osäkert i programmeringsspråk eller hårdvaruplattformar som letar efter aritmetiska spill .
Det senare alternativet fungerar bara med typer som stöder bitvisa operationer (till exempel kommer C#double -kompilatorn inte att tillåta dig att utbyta variabelvärden på detta sätt).
Vissa språk (som PHP ) har konstruktioner för att simulera parallell tilldelning:
lista ( $a , $b ) = array ( $b , $a );Vissa programmeringsspråk, som C++ , tillåter villkorliga mål i tilldelningssatser. Till exempel uttrycket:
( flagga ? count1 : count2 ) = 0 ;kommer att tilldela ett värde till 0variabeln count1if , och if . flag==truecount2flag==false
En annan variant av villkorlig tilldelning ( Ruby ):
a ||= 10Denna konstruktion tilldelar ett avärde till en variabel endast om värdet ännu inte har tilldelats eller är lika med false.
Operatorn för sammansatt tilldelning låter dig förkorta en vanlig tilldelningsform. Med den här metoden kan du förkorta notationen för en tilldelning som använder målvariabeln som den första operanden på höger sida av uttrycket, till exempel:
a = a + bSyntaxen för C -sammansättningstilldelningsoperatorn är föreningen av den önskade binära operatorn och =. Till exempel är följande poster likvärdiga
sum += value; | sum = sum + value; |
Programmeringsspråk som stöder sammansatta operatorer ( C++ , C# , Python , Java , etc.) har vanligtvis versioner för de flesta av dessa språks binära operatorer+= ( , -=, &=etc.).
På språken i C- familjen finns det fyra unära (det vill säga med ett argument) aritmetiska operatorer för att öka och minska tal med en: två " "-operatorer och två " "-operatorer. Operatorer kan skrivas före operanden (prefix) eller efter den (postfix eller suffix). Prefix- och postfix-operatorer skiljer sig åt i utvärderingsordningen. Prefixoperatorer ändrar ett nummer med ett och returnerar det ändrade numret. Postfix-operatörer lagrar ett nummer i en temporär variabel, ändrar det ursprungliga numret och returnerar värdet på den temporära variabeln. ++--
Ett exempel på användning av operatorn : ++
Öka värdet på en variabel med en | Motsvarande notation |
---|---|
count ++; | count = count + 1; |
Även om det inte ser ut som ett uppdrag, det är det. Resultatet av att utföra ovanstående uttalande är detsamma som resultatet av att utföra uppdraget.
Operatörerna " " kallas inkrementoperatorer och " "-operatorerna kallas dekrementoperatorer. Operatorer används ofta i C-språket när de hanterar pekare och arrayindex . ++--
Funktionen hos moderna datorer består av att läsa data från minnet eller enheten till register, utföra operationer på dessa data och skriva till minnet eller enheten. Huvudoperationen här är dataöverföring (från register till minne, från minne till register, från register till register). Följaktligen uttrycks det direkt av instruktionerna från moderna processorer . Så för x86- arkitekturen (alla kommandon nedan gäller också för denna arkitektur), är detta en operation movoch dess varianter för att skicka data av olika storlekar. Tilldelningsoperationen (överföring av data från en minnescell till en annan) implementeras praktiskt taget direkt av detta kommando. Generellt sett krävs två instruktioner för att utföra en dataöverföring i minnet: en minne-till-register-flyttning och en register-till-minne-flyttning, men med optimeringar kan antalet instruktioner minskas i de flesta fall.
movl -4(%ebp), %eax instruktioner för tilldelning |