C Как определить rvalue и lvalue?
В C есть ли способ определить значения и значения?
Некоторые из них легко идентифицировать, например, в присваивании левое значение равно lvalue, а правое значение - rvalue.
Но в других сценариях отождествление с таким правилом затруднительно.
Например: *p++
а также i++
(где p - указатель на целое число, а i - целое число) - как определить, является ли это rvalue или lvalue? Контекст ++*p++
работает пока ++i++
не с i++
это значение (как говорят серьезные парни).
Как определить rvalue и lvalue в выражении?
3 ответа
lvalue (от значения левой стороны (LHS)) в чем-то, что относится к памяти (или регистру) и которому вы можете присвоить значения. *p++
является lvalue, так как это разыменованный указатель (то есть относится к месту в памяти, которое ptr
указывает на то, что значение ptr
сам адрес этого места) и ++*ptr++
на самом деле означает: *ptr = *ptr + 1; ptr = ptr + 1;
- увеличивает значение, на которое указывает ptr
а затем увеличивает значение самого указателя. i++
не является lvalue, так как это значение i
увеличивается на 1 и не относится к месту в памяти. Вы можете думать о таких значениях как о заключительных - они не могут быть далее изменены и могут использоваться только как значения для присвоения lvalue. Вот почему они называются rvalues (с правой стороны (RHS) значение).
LHS и RHS относятся к обеим сторонам выражения присваивания A = B;
, A
это LHS и B
это RHS.
Термин lvalue был с C (и был перенесен в C++ и расширен позже). Для начала не было никакого значения. В черновике, который у меня есть (N1570), есть два вхождения термина rvalue - один раз в сноске № 64 и один раз в индексе.
В двух словах: в мире Си у вас есть два типа объектов - lvalues и все остальное.
Обратите внимание, что сноски не являются частью стандарта, но они могут предоставить некоторые полезные идеи. Здесь идет сноска 64:
64) Имя lvalue происходит от выражения присваивания E1 = E2, в котором левый операнд E1 должен быть (модифицируемым) lvalue. Возможно, это лучше рассматривать как представление объекта "значение локатора". То, что иногда называют "rvalue", в этом международном стандарте описывается как "значение выражения".
Очевидным примером lvalue является идентификатор объекта. В качестве еще одного примера, если E является унарным выражением, которое является указателем на объект, *E является l-значением, которое обозначает объект, на который указывает E.
Это дает хорошее начало. Теперь имейте в виду, что выражение построено из объектов (и операторов, но мы немного разберемся с ними), и есть две фундаментальные вещи, о которых вам нужно беспокоиться при работе с объектами: тип и значение. Давайте посмотрим, что стандарт говорит об ограничениях типов (6.3.2.1/p1):
Lvalue - это выражение (с типом объекта, отличным от void), которое потенциально обозначает объект;64) если lvalue не обозначает объект при его оценке, поведение не определено.
Также обратите внимание на следующую строку, которая важна:
Когда говорят, что объект имеет определенный тип, тип определяется значением l, используемым для обозначения объекта.
Таким образом, lvalue может использоваться вместо типа (мы тоже это увидим). Далее, давайте взглянем на контексты, в которых объект является lvalue (6.3.2.1/2):
когда это операнд оператора sizeof, оператора _Alignof, унарного оператора &, оператора ++, оператора - или левого операнда оператора. оператор или оператор присваивания
Итак, это операторы, за которыми вам нужно следить. Во всех остальных случаях:
lvalue, у которого нет типа массива, преобразуется в значение, хранящееся в назначенном объекте (и больше не является lvalue); это называется преобразованием lvalue.
Существует два специальных типа: массивы и функциональные обозначения. Эти затухания, т. Е. Преобразуются в выражение с типом указатель на тип, которое указывает на начальный элемент объекта массива и не является lvalue / "указателем на функцию, возвращающую тип". (Помните, мы остановились на том факте, что lvalues может работать как типы - это именно то, что они делают с sizeof
а также _Alignof
!)
От Дейтеля и Дейтеля:
Имена переменных называются lvalues (для "левых значений"), потому что они могут использоваться слева от оператора присваивания. Константы называются rvalues (для "правильных значений"), потому что они могут использоваться только в правой части оператора присваивания. Обратите внимание, что значения l также могут использоваться как значения r, но не наоборот.
x = 3; /* here, x is an lvalue */
c = x; /* and in the next line it is an rvalue */