Исторически в языке C различие между lvalue и rvalue возникло как основа для определения того, к каким выражениям можно применять оператор присваивания. Lvalue (left value) обозначает объект, который занимает определённое место в памяти и к которому можно применить операцию взятия адреса (&). Rvalue (right value) — это временное значение, не имеющее определённого адреса. Это различие важно для понимания, как работает присваивание, передача аргументов в функцию и оптимизация компилятором.
Проблема возникает при попытке выполнять операции, допустимые только над lvalue (например, присваивание), для rvalue, и наоборот. Это может приводить к ошибкам компиляции или к неочевидным багам уже на этапе выполнения.
Решение заключается в чёткого понимания контекстов использования lvalue и rvalue. Пример:
int x = 5; int y; y = x; // x — lvalue и rvalue, y — lvalue y = x + 1; // x + 1 — rvalue (нельзя взять адрес) // &x — корректно, &(x + 1) — ошибка
Ключевые особенности:
Можно ли взять адрес от любого выражения, например от выражения (x + y)?
Нет, только lvalue имеет адрес. Например, выражение (x + y) — это rvalue.
int z = 3, y = 7; int *p = &(z + y); // Ошибка компиляции
Что произойдет при попытке присвоить значение константе (например, 5 = x)?
Произойдет ошибка компиляции, потому что литерал 5 — rvalue и не может выступать в качестве lvalue.
5 = x; // Ошибка: левый операнд не является lvalue
Может ли функция возвращать lvalue?
Обычная функция возвращает rvalue, но если возвращается ссылка (например, в C++), то это lvalue. В C допустимы только возвраты rvalue.
int foo() { int x = 5; return x; } // Возвращается rvalue
Программист попытался взять адрес у временного результата выражения (a + b):
int *p = &(a + b);
Плюсы:
Минусы:
Программист осознанно отделяет lvalue и rvalue. Использует значения только там, где разрешет синтаксис и семантика:
int x = 7; int *p = &x; // корректно
Плюсы:
Минусы: