Кто-нибудь объяснит левое значение и правильное значение на уровне ассемблера?

Я думаю, что все здесь знают, что --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, я не знаю, почему это не так и почему возвращаемое значение является временной переменной. Как компилятор принимает такое решение? Кто-нибудь может дать мне некоторое объяснение на уровне ассемблера? Спасибо!

2

Решение

Оставленное значение? Правильное значение?

Если вы говорите о 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.

7

Другие решения

[Примечание: я отвечаю на это с точки зрения C ++.]

Если предположить, i это встроенный тип, если вы просто пишете --i; или же i--; а не, скажем, j = ++i; или же j = i++;тогда неудивительно, что они компилируются компилятором в код сборки — они делают то же самое, что уменьшает i, Разница становится очевидной только на уровне сборки, когда вы что-то делаете с результатом, иначе они фактически имеют одинаковую семантику.

(Обратите внимание, что если бы мы думали о перегруженных операторах до и после декремента для пользовательского типа, сгенерированный код не будет таким же.)

Когда ты пишешь что-то вроде i-- = 5;компилятор совершенно справедливо жалуется, потому что семантика пост-декремента по существу сводится к уменьшению предмета, о котором идет речь, но возвращает его старое значение для дальнейшего использования. Возвращенная вещь будет временной, поэтому i-- дает значение r.

1

Термины «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».

0
По вопросам рекламы [email protected]