Интуитивно говоря, lvalue - это выражение, которое означает «вот объект честности к добру», тогда как rvalue - это выражение, которое означает «вот значение, которое не является объектом». (Различие немного мрачнее этого, но на данный момент это разумное упрощение.) То есть lvalue - это поле, в которое можно что-то поместить, а rvalue - это просто значение в этом поле.
Например, выражение 137
является значением r, так как это чистое число, а не поле, содержащее число. Если x
является переменной, то выражение x
является lvalue, потому что оно представляет поле и число в нем, но выражение x + 137
является rvalue, поскольку оно просто представляет число, а не поле, содержащее число.
Как это учитывается? Предположим, что x
является int
. Тогда (float)x
является r-значением, потому что оно просто представляет значение - в частности, это «число, которое вы получили бы, если бы взяли x
и сделали наименее ужасную вещь, которую можно представить как float
». Это не коробка, в которую можно что-то положить.
Это объясняет, почему
(int *)p = &x;
не работает. (int *)p
- это r-значение - чистое значение, а не то, что вы можете присвоить - и вы пытаетесь воспринимать это как поле, в которое вы можете поместить что-то.
С другой стороны, результат разыменование указателя является lvalue, так как оно представляет «посмотрите туда! это поле, в которое вы можете что-то вставить». В этом смысле вы можете написать
*(int *)p = x;
, потому что это означает
- Оценить выражение
(int *)p
. Хорошо, теперь у нас есть значение r, и это указатель на где-то в памяти, который содержит int
. - Разыменование этого указателя для получения
*(int *)p
. Хорошо, теперь у нас есть lvalue, представляющее это целое число. - Материал
x
в этом месте. Это нормально - *(int *)p
- это поле.
Могут быть и другие проблемы с этим кодом:
*(int *)p = &x;
Здесь RHS этого выражения имеет тип (int *)
, который является указателем. LHS этого выражения имеет тип int
(вы разыменовали указатель на целое число), поэтому вы конвертируете указатель в целое число. Таким образом, это означает, что либо отсутствует приведение, либо вы делаете неправильную вещь с этим кодом.
Вторая проблема заключается в том, что
*(int *)p
означает "интерпретировать p
как хотя он говорит вам, где в памяти можно найти int
, тогда напишите что-нибудь там. " Если p
не указатель, это почти наверняка приведет к взлому sh вашего кода, потому что вы будете писать в произвольном месте в памяти. И если p
является указателем, то, возможно, лучше разыграть &x
, чем p
?
p = (T*) &x;
, где T
- это тип указанной вещи по p
.