Ce este lvalue și rvalue, braț, programare

Dacă aveți ceva timp pentru a programa în C sau în C ++, atunci probabil ați auzit de termenii lvalue (a se citi ca „EL-valoare“) și rvalue (a se citi ca „AR-value“), deoarece acestea apar, uneori, în mesaje de eroare de compilare . Există, de asemenea, o șansă să nu aveți o înțelegere clară a ceea ce înseamnă totul.







Având în vedere ambiguitatea în definițiile pentru lvalue și rvalue printre standardele lingvistice, nu sunt pregătit să ofer definiții exacte. Cu toate acestea, pot explica baza de bază a conceptelor comune tuturor standardelor.

Ce este lvalue și rvalue, braț, programare

Așa cum se întâmplă adesea cu conceptele limbajului obscur, puteți să vă puneți întrebarea - de ce ar trebui să vă interesați de cunoașterea semnificației lvalue și rvalue? Prin toate conturile, dacă scrieți numai la C, atunci puteți lucra fără să înțelegeți care sunt esența lvalurilor și rvalurilor. Mulți programatori fac acest lucru. Însă înțelegerea lvalurilor și rvalurilor oferă o perspectivă valoroasă asupra comportamentului operatorilor încorporați și a codului generat de compilator pentru executarea acestor operatori. Dacă programați în C ++, înțelegerea activității operatorilor încorporați oferă o bază obligatorie pentru scrierea calitativă a operatorilor supraîncărcați.

Kernighan si Ritchie a introdus termenul lvalue, pentru a separa una de cealaltă expresie. În cartea sa „C Programming Language“ (Prentice-Hall, 1988), au scris: „Obiectul este manipulat zona de depozitare; lvalue este o expresie care se referă la numele obiectului«lvalue»a fost de la expresia de atribuire E1 = E2, în care stânga operandului E1. trebuie să fie o expresie de tip lvalue ".

Ce este lvalue și rvalue, braț, programare

Cu alte cuvinte, operanzii din stânga și din dreapta din expresia de atribuire sunt expresii independente. Pentru ca asignarea să fie valabilă, operandul stâng trebuie să se refere la un obiect care trebuie să fie de tip lvalue. Operandul potrivit poate fi orice expresie, nu trebuie sa aiba proprietati de tip lvalue. De exemplu:

declară n ca un obiect de tip int. Când utilizați n într-o expresie de atribuire, cum ar fi:

n este o expresie (mai exact o subexpresie a unei expresii de atribuire) referindu-se la un obiect int. Expresia pentru n este lvalue.

Să presupunem că ați rearanjat operatorii din stânga și din dreapta:

Daca nu sunteti un fost programator Fortran, este evident că este cel mai tâmpit lucru pe care îl pot face. Tipul Assignment 3 = n încercări de a schimba valoarea unei constante întreg. Din fericire, C și compilator C ++ nu permite acest lucru și va emite o eroare. (Notă traducător :. Pentru că, uneori, fac greșeli atunci când scrieți în mod accidental în loc de semn == = atribuire, în special în operatorul == controalele puse stânga constantă, compilatorul a raportat o eroare în cazul în care cesiunea este o lvaloare pe stânga, atunci compilatorul nu va observa un truc murdar. și nu au raportat o eroare) a respins o astfel de operațiune -. că operandul stâng 3 în expresia nu este o lvaloare. Este un rvalue și nu se referă la un obiect; pur și simplu reprezintă un anumit sens.

Nu știu de unde a venit termenul rvalue. Nici unul dintre standardele C nu îl folosește, cu excepția notei de subsol unde se afirmă că "uneori standardul descrie rvalue ca" valoarea expresiei ".

Specificația standard C ++ utilizează termenul rvalue, definindu-l indirect în următoarea afirmație: "Fiecare expresie se referă fie la valoarea lvalue, fie la rvalue." Astfel, rvalue este orice expresie care nu este lvalue.

Literele digitale, cum ar fi 3 și 3.14159, sunt rvalue. Caracterele de caractere, cum ar fi "a", se referă, de asemenea, la rvalue. Identificator care se referă la obiectul este lvalue, ci un identificator de denumire constantelor enumerate (constanta enumerare), este un rvalue. De exemplu:

Cea de-a doua atribuire va provoca o eroare la compilare, deoarece albastrul este rvalue.

Deși nu puteți folosi rvalue ca lvalue, puteți utiliza lvalue ca rvalue. De exemplu, definiți:

Puteți atribui o valoare de n unui obiect care este notat cu m:

Această expresie folosește expresia lvalue ca rvalue. Strict vorbind, compilatorul realizează ceea ce numește standardul C ++ standard pentru conversia de la rvalue lvalue (lvalue-to-rvalue conversie), pentru a primi valoarea stocată în obiectul, care este notat cu n.

[Lvalue cu alte cuvinte]

Deși lvalue și rvalue a primit numele lor pe rolurile lor (poziții), într-o expresie de atribuire, conceptul de conceptele utilizate în toate expresiile, chiar și cele care implică alți operatori integrați.

De exemplu, ambii operanzi ai operatorului binar încorporat + trebuie să fie expresii. Evident, aceste expresii trebuie să aibă tipuri adecvate. După conversii, ambele expresii trebuie să aibă același tip aritmetic, sau o expresie trebuie să aibă un tip de pointer, iar cealaltă trebuie să aibă un tip întreg. Dar oricare dintre ele poate fi lvalue sau rvalue. Astfel, ambele expresii x + 2 și 2 + x sunt admisibile.







Deși operanzii operatorului binar + pot fi lvalue, rezultatul este întotdeauna rvalue. De exemplu, obiectele întregi m și n sunt date, iar următoarea expresie va duce la o eroare:

Operatorul + are o prioritate mai mare decât operatorul =. Astfel, expresia de asignare este echivalentă cu următoarea:

Eroarea apare deoarece m + 1 este rvalue.

Deși unar necesită lvalue ca operand, rezultatul va fi rvalue. exemplu:

Spre deosebire de Unar , unar * returnează lvalue ca rezultat. Un pointer non-zero p indică întotdeauna un obiect, deci * p este un lvalue. exemplu:

Deși rezultatul unar * este lvalue, operandul său poate fi rvalue, astfel:

[Stocare date pentru rvalue]

În conceptul de bază, rvalue este pur și simplu o valoare, nu se referă la obiect. În practică, rvalue se poate referi la un obiect. Pur și simplu nu este necesar ca rvalue să se refere la un obiect. Prin urmare, atât C, cât și C ++ insistă să programați, ca și cum rvalue nu se referă la obiecte.

Presupunerea că rvalue nu se referă la un obiect, oferă C și C + + compilatoare o libertate considerabilă în generarea de cod pentru expresii rvalue. Să presupunem că există o atribuire de acest tip:

Aici, n este un intreg (int). Compilatorul poate genera un magazin de date numit inițializat cu o valoare de 1, ca în cazul în care 1 a fost un lvalue. Aceasta ar genera codul pentru a copia din depozitul inițializat în locația de stocare alocată pentru n. În limbajul de asamblare, aceasta ar putea arăta astfel:

În acest caz, rvalue 1 nu apare niciodată ca un obiect în spațiul de date. Mai degrabă, acesta apare ca instrucțiuni în spațiul de cod.

La unele procesoare, cel mai rapid mod de a pune o valoare de 1 în obiect este să îl ștergeți și apoi să creșteți, de exemplu:

Ștergerea obiectului își stabilește conținutul la 0. Randamentele de creștere 1. Cu toate acestea, datele care reprezintă valorile 0 și 1 nu apar nicăieri în cod.

Deși este adevărat că rvalue în C nu se referă la obiecte, pentru C ++ nu este. În C ++, rvalue, având un tip de clasă, se referă la obiecte, dar acestea nu sunt încă lvalue. Deci tot ceea ce am spus pentru rvalue este adevărat până când avem de-a face cu un tip de clasă rvalue.

Deși lvalue denumește obiecte, nu toate lvaluele pot apărea ca partea stângă a operatorului de atribuire. După cum știți, expresia este o secvență de operatori și operanzi care specifică unele calcule. Calculele pot avea ca rezultat o valoare sau pot produce efecte secundare. Atributul de exprimare are forma

unde e1 și e2 sunt ele însele expresii. Operatorul corect e2 poate fi orice expresie, totuși, operandul stâng trebuie să fie o expresie a lvaluei, trebuie să se refere la obiect (aceasta a fost menționată mai devreme).

Cuvântul const constă în conceptul de bază al lvaluei semantică inadecvată a expresiilor. Trebuie să putem distinge între diferitele tipuri de lvalue.

Calificatorul const apare în declarație și modifică tipul din declarație sau o parte din ea, de exemplu:

Aici este declarat un obiect de tipul "const int" (constant constant). Expresia n se referă la un obiect, ca și când nu există o constantă, cu excepția faptului că n se referă la un obiect pe care programul nu îl poate schimba. De exemplu, o atribuire de acest tip va produce o eroare de compilare:

Cum se deosebește o expresie care se referă la un obiect const, cum ar fi n, de ceva din rvalue? La urma urmei, dacă vom rescrie aceste expresii pentru un întreg literal în loc de n. atunci primim aceeași eroare:

atunci următoarea atribuire va duce la o eroare:

Deoarece forța compilatorului nu se plânge de conversie, nu se dorește ceva asemănător, altfel poate provoca greșeli greu de găsit.

Astfel, expresia care se referă la obiectul const este într-adevăr lvalue, nu rvalue. Cu toate acestea, acest lucru este un tip special de lvalue, numită lvalue non-modificabil (lvalue non-modificabil), care nu poate fi folosit pentru a modifica obiectul referite de lvalue. Acest lucru este în contrast cu lvalue accesibil (care este declarat fără const), pe care îl puteți folosi pentru a schimba obiectul referit de lvalue.

Din moment ce factorul de prezență al cuvântului cheie const poate influența acum, nu mai este exact să se apeleze la partea stângă a operatorului de atribuire, ca lvalue. Mai degrabă, ar trebui să fie numit posibil să se schimbe lvalue (lvalue modificabilă). De fapt, fiecare operator de atribuire aritmetică, cum ar fi + = și * =, are nevoie de o lvală modificabilă ca operand stânga. Pentru toate tipurile scalare:

cu excepția faptului că x este evaluată o singură dată. Deoarece x în această atribuire aritmetică trebuie să fie o lvală care trebuie modificată, trebuie să existe și o simplă atribuire. Nu toți operatorii care necesită un operand lvalue necesită exact modificarea lvaluei. Operator unar acceptă atât lvalue modificabilă, cât și lvalue nemodificabilă ca operand. De exemplu, dacă:

m este o expresie validă care returnează tipul "pointer to int", și n este, de asemenea, o expresie validă care returnează rezultatul "pointer to const int".

[Ceea ce este într-adevăr nemodificat]

Anterior am spus că un lvalue care nu poate fi modificat este o lvalue pe care nu o poți folosi pentru a schimba un obiect. Observați că nu am spus că lvalue non-modificabil se referă la un obiect care nu se poate schimba - am spus că nu puteți utiliza un lvalue, pentru a schimba subiectul. (Nota traducătorului: Îmi place aceste puzzle-uri izvraschenskih limbaj C) Diferența este subtilă, dar totuși important, după cum va fi prezentat în exemplul următor. Să presupunem:

Aici avem punctele p la n, deci atat * p, si doar n sunt doua expresii diferite care se refera la acelasi obiect. Cu toate acestea, * p și n au diferite tipuri. Așa cum sa discutat în articolul „Ce Const înseamnă cu adevărat“ (Ceea ce înseamnă cu adevărat const) [1], folosește conversie de calificare de atribuire, pentru a converti o valoare de tip „pointer la int“, în valoarea de „pointer la const int“ tipul. Expresia n este de tip "not const int". Aceasta este o lvalue modificabilă, astfel încât să puteți modifica obiectul pe care îl indică:

Pe de altă parte, p este de tip "pointer to const int", deci * p este de tip const int. Expresia * p este o lvalue nemodificată și nu puteți folosi * p pentru a modifica n (chiar dacă puteți folosi n pentru a modifica):

Aceasta este semantica determinării constantului const în C și C ++.

Fiecare expresie C și C ++ este fie lvalue, fie rvalue. Lvalue este o expresie care denotă un obiect (se referă la acesta cu sau fără un pointer). Fiecare lvalue este, la rândul său, fie modificabil, fie nemodificabil. Rvalue se referă la orice expresie care nu este lvalue. Din punct de vedere operațional, diferențele dintre rvalue și lvalue pot fi reduse la teze:

Și din nou, așa cum am menționat deja, toate acestea se aplică numai la rvalue de tip non-clasa. Clasele din C ++ distrug aceste concepte chiar mai mult.

1. Site-uri Lvalues ​​și Rvalues: embedded.com.
2. Site-ul Lvalues ​​nemodificabil: embedded.com.







Trimiteți-le prietenilor: