Historycznie w języku C różnica między lvalue a rvalue powstała jako podstawa do określenia, do jakich wyrażeń można stosować operator przypisania. Lvalue (left value) oznacza obiekt, który zajmuje określone miejsce w pamięci i do którego można zastosować operację uzyskania adresu (&). Rvalue (right value) to wartość tymczasowa, która nie ma określonego adresu. Ta różnica jest ważna dla zrozumienia, jak działa przypisanie, przekazywanie argumentów do funkcji i optymalizacja przez kompilator.
Problem pojawia się przy próbie wykonywania operacji, które są dozwolone tylko dla lvalue (na przykład przypisanie), dla rvalue, i odwrotnie. Może to prowadzić do błędów kompilacji lub nieoczywistych błędów już na etapie wykonywania.
Rozwiązanie polega na wyraźnym zrozumieniu kontekstów użycia lvalue i rvalue. Przykład:
int x = 5; int y; y = x; // x — lvalue i rvalue, y — lvalue y = x + 1; // x + 1 — rvalue (nie można uzyskać adresu) // &x — poprawne, &(x + 1) — błąd
Kluczowe cechy:
Czy można uzyskać adres z dowolnego wyrażenia, na przykład z wyrażenia (x + y)?
Nie, tylko lvalue ma adres. Na przykład wyrażenie (x + y) — to rvalue.
int z = 3, y = 7; int *p = &(z + y); // Błąd kompilacji
Co się stanie, gdy spróbujesz przypisać wartość do stałej (na przykład, 5 = x)?
Pojawi się błąd kompilacji, ponieważ literał 5 — rvalue i nie może występować jako lvalue.
5 = x; // Błąd: lewy operand nie jest lvalue
Czy funkcja może zwracać lvalue?
Zwykła funkcja zwraca rvalue, ale jeśli zwracany jest odnośnik (na przykład w C++), to jest to lvalue. W C dopuszczalne są tylko zwroty rvalue.
int foo() { int x = 5; return x; } // Zwracane jest rvalue
Programista próbował uzyskać adres z tymczasowego wyniku wyrażenia (a + b):
int *p = &(a + b);
Zalety:
Wady:
Programista świadomie rozdziela lvalue i rvalue. Używa wartości tylko tam, gdzie pozwala na to składnia i semantyka:
int x = 7; int *p = &x; // poprawne
Zalety:
Wady: