Storicamente, nel linguaggio C, la distinzione tra lvalue e rvalue è emersa come base per determinare quali espressioni possono essere utilizzate con l'operatore di assegnazione. Lvalue (left value) indica un oggetto che occupa un determinato spazio nella memoria e a cui è possibile applicare l'operazione di accesso all'indirizzo (&). Rvalue (right value) è un valore temporaneo che non ha un indirizzo definito. Questa distinzione è importante per comprendere come funziona l'assegnazione, la trasmissione degli argomenti nelle funzioni e l'ottimizzazione del compilatore.
I problemi sorgono quando si tenta di eseguire operazioni consentite solo sugli lvalue (ad esempio, l'assegnazione) per gli rvalue e viceversa. Questo può portare a errori di compilazione o a bug poco evidenti già in fase di esecuzione.
La soluzione consiste in una chiara comprensione dei contesti di utilizzo di lvalue e rvalue. Esempio:
int x = 5; int y; y = x; // x è sia lvalue che rvalue, y è lvalue y = x + 1; // x + 1 è rvalue (non si può prendere l'indirizzo) // &x è corretto, &(x + 1) è errore
Caratteristiche chiave:
È possibile prendere l'indirizzo di qualsiasi espressione, ad esempio dell'espressione (x + y)?
No, solo lvalue ha un indirizzo. Ad esempio, l'espressione (x + y) è rvalue.
int z = 3, y = 7; int *p = &(z + y); // Errore di compilazione
Cosa succede se si tenta di assegnare un valore a una costante (ad esempio, 5 = x)?
Si verifica un errore di compilazione, perché il letterale 5 è un rvalue e non può agire come lvalue.
5 = x; // Errore: l'operando sinistro non è un lvalue
Può una funzione restituire un lvalue?
Una funzione normale restituisce un rvalue, ma se viene restituito un riferimento (ad esempio, in C++), allora è un lvalue. In C sono consentiti solo i ritorni di rvalue.
int foo() { int x = 5; return x; } // Viene restituito un rvalue
Un programmatore ha tentato di prendere l'indirizzo di un risultato temporaneo dell'espressione (a + b):
int *p = &(a + b);
Vantaggi:
Svantaggi:
Un programmatore separa consapevolmente lvalue e rvalue. Usa valori solo dove lo consente la sintassi e la semantica:
int x = 7; int *p = &x; // corretto
Vantaggi:
Svantaggi: