Historisch gesehen entstand die Unterscheidung zwischen lvalue und rvalue in der Programmiersprache C als Grundlage zur Bestimmung, auf welche Ausdrücke der Zuweisungsoperator angewendet werden kann. Lvalue (left value) bezeichnet ein Objekt, das einen bestimmten Speicherort einnimmt und auf das die Adresse (&) angewendet werden kann. Rvalue (right value) ist ein temporärer Wert ohne spezifische Adresse. Diese Unterscheidung ist wichtig, um zu verstehen, wie Zuweisungen, Argumentübergabe an Funktionen und Compileroptimierungen funktionieren.
Das Problem tritt auf, wenn versucht wird, Operationen, die nur auf lvalue erlaubt sind (zum Beispiel Zuweisungen), auf rvalue auszuführen und umgekehrt. Dies kann zu Kompilierungsfehlern oder zu unklaren Bugs bereits zur Laufzeit führen.
Die Lösung besteht im klaren Verständnis der Kontexte, in denen lvalue und rvalue verwendet werden. Beispiel:
int x = 5; int y; y = x; // x ist sowohl lvalue als auch rvalue, y ist lvalue y = x + 1; // x + 1 ist rvalue (Adresse kann nicht genommen werden) // &x ist korrekt, &(x + 1) ist ein Fehler
Wichtige Merkmale:
Kann man die Adresse von jedem Ausdruck nehmen, z.B. von dem Ausdruck (x + y)?
Nein, nur lvalue hat eine Adresse. Zum Beispiel ist der Ausdruck (x + y) ein rvalue.
int z = 3, y = 7; int *p = &(z + y); // Kompilierungsfehler
Was passiert, wenn man versucht, einer Konstante einen Wert zuzuweisen (zum Beispiel, 5 = x)?
Es tritt ein Kompilierungsfehler auf, weil das Literal 5 ein rvalue ist und nicht als lvalue verwendet werden kann.
5 = x; // Fehler: linker Operand ist kein lvalue
Kann eine Funktion lvalue zurückgeben?
Eine normale Funktion gibt rvalue zurück, aber wenn ein Verweis zurückgegeben wird (z.B. in C++), dann ist das lvalue. In C sind nur Rückgaben von rvalue zulässig.
int foo() { int x = 5; return x; } // gibt rvalue zurück
Ein Programmierer versuchte, die Adresse eines temporären Ergebnisses eines Ausdrucks (a + b) zu nehmen:
int *p = &(a + b);
Vorteile:
Nachteile:
Ein Programmierer trennt lvalue und rvalue bewusst. Er verwendet Werte nur dort, wo es die Syntax und Semantik erlaubt:
int x = 7; int *p = &x; // korrekt
Vorteile:
Nachteile: