Я думаю, что все здесь знают, что --i
является левым значением выражения в то время как i--
является правильным значением выражения. Но я прочитал ассемблерный код двух выражений и выяснил, что они скомпилированы в один и тот же ассемблерный код:
mov eax,dword ptr [i]
sub eax,1
mov dword ptr [i],eax
В стандарте языка C99 значение l определяется для выражения с типом объекта или неполным типом, отличным от void.
Так что я могу гарантировать, что --i
вернуть значение, тип которого отличается от void i--
вернуть значение, которое является недействительным или, возможно, временной переменной.
Однако, когда я даю задание, такое как i--=5
, компилятор выдаст мне ошибку, указывающую i--
не является lvalue, я не знаю, почему это не так и почему возвращаемое значение является временной переменной. Как компилятор принимает такое решение? Кто-нибудь может дать мне некоторое объяснение на уровне ассемблера? Спасибо!
Оставленное значение? Правильное значение?
Если вы говорите о lvalues а также rvalues, тогда свойство lvalue или rvalue применяется к результат выражения, что означает, что вы должны учитывать результаты --i
а также i--
, И на языке Си и то и другое --i
а также i--
являются значениями. Итак, ваш вопрос основан на неверной предпосылке в области языка Си. --i
является не lvalue в C. Я не знаю, что вы пытаетесь сделать, ссылаясь на стандарт C99, поскольку в нем четко указано, что ни один из них не является lvalue. Кроме того, не ясно, что вы подразумеваете под i--
возвращая void
, Нет, встроенный постфикс --
никогда не вернется void
,
Различие между lvalue и rvalue в случае --i
а также i--
существует только в C ++.
Во всяком случае, если вы смотрите на простой --i;
а также i--;
выражения выражений, вы не используете результаты этих выражений. Вы отбрасываете их. Единственный пункт, чтобы использовать автономный --i
а также i--
это их побочные эффекты (уменьшение i
). Но поскольку их побочные эффекты идентичны, вполне ожидаемо, что сгенерированный код будет таким же.
Если вы хотите увидеть разницу между --i
а также i--
Выражения, вы должны использовать их результаты. Например
int a = --i;
int b = i--;
будет генерировать разные коды для каждой инициализации.
Этот пример не имеет ничего общего с оценкой или оценкой их результатов. Если вы хотите увидеть разницу с той стороны (которая существует только в C ++, как я уже говорил выше), вы можете попробовать это
int *a = &--i;
int *b = &i--;
Первая инициализация будет компилироваться в C ++ (поскольку результатом является lvalue), а вторая не будет компилироваться (поскольку результатом является rvalue, и вы не можете применить встроенный унарный код &
к значению).
Обоснование этой спецификации довольно очевидно. Так как --i
оценивает до новый ценность i
, вполне возможно заставить этот оператор возвращать ссылку на i
сам как его результат (и язык C ++, в отличие от C, предпочитает возвращать lvalue, когда это возможно). В то же время, i--
требуется вернуть старый ценность i
, Поскольку к тому времени мы получаем, чтобы проанализировать результат о i--
i
Сам, вероятно, проведет новый значение, мы не можем вернуть ссылку на i
, Мы должны сохранить (или воссоздать) старое значение i
в каком-то вспомогательном временном местоположении и вернуть его в результате i--
, Это временное значение — это просто значение, а не объект. Он не должен находиться в памяти, поэтому он не может быть lvalue.
Если предположить, i
это встроенный тип, если вы просто пишете --i;
или же i--;
а не, скажем, j = ++i;
или же j = i++;
тогда неудивительно, что они компилируются компилятором в код сборки — они делают то же самое, что уменьшает i
, Разница становится очевидной только на уровне сборки, когда вы что-то делаете с результатом, иначе они фактически имеют одинаковую семантику.
(Обратите внимание, что если бы мы думали о перегруженных операторах до и после декремента для пользовательского типа, сгенерированный код не будет таким же.)
Когда ты пишешь что-то вроде i-- = 5;
компилятор совершенно справедливо жалуется, потому что семантика пост-декремента по существу сводится к уменьшению предмета, о котором идет речь, но возвращает его старое значение для дальнейшего использования. Возвращенная вещь будет временной, поэтому i--
дает значение r.
Термины «lvalue» и «rvalue» происходят из выражения присваивания E1 = E2
, в котором левый операнд E1
используется для идентификации объекта, который нужно изменить, и правого операнда E2
определяет значение, которое будет использоваться. (См. C 1999 6.3.2.1, примечание 53.)
Таким образом, выражение, которое все еще имеет некоторый объект, связанный с ним, может использоваться, чтобы найти этот объект и записать в него. Это lvalue. Если выражение не является lvalue, его можно назвать rvalue.
Например, если у вас есть i
, имя некоторого объекта, это lvalue, потому что мы можем найти где i
есть, и мы можем назначить ему, как в i = 3
,
С другой стороны, если у нас есть выражение i+1
, тогда мы взяли значение i и добавили 1, и теперь у нас есть значение, но оно не связано с конкретным объектом. Это новое значение не в i
, Это просто временное значение и не имеет определенного местоположения. (Конечно, компилятор должен поместить его куда-нибудь, если оптимизация полностью не удалит выражение. Но оно может быть в регистрах и никогда не в памяти. Даже если оно по какой-то причине находится в памяти, язык C не предоставляет вам способа чтобы узнать, где.) Так i+1
не является lvalue, потому что вы не можете использовать его в левой части задания.
--i
а также i++
оба выражения, которые являются результатом принятия значения i
и выполняя некоторую арифметику. (Эти выражения также меняются i
, но это побочный эффект оператора, а не часть результата, который он возвращает.) «Слева» и «справа» от lvalues и rvalues не имеют ничего общего с тем, --
или же ++
оператор находится слева или справа от имени; они имеют отношение к левой стороне или правой стороне задания. Как объясняют другие ответы, в C ++, когда они находятся слева от lvalue, они возвращают lvalue. Тем не менее, это случайно; это определение операторов в C ++ появилось много лет спустя после создания термина «lvalue».