15

Почему ++i считается lvalue, а i++ rvalue?
Я нашел ответ на данный вопрос на stackoverflow, но мой ужасный английский не позволяет мне грамотно в этом разобраться. Ведь приоритет префиксного и постфиксного ++ всё равно выше, чем & и по идее в любом случае будет сначала ++, а только потом & или я вообще не так понимаю?

G71
  • 2,089

6 Answers6

11

Возможно, это очень неполный ответ по сравнению с развёрнутым первым по ссылке, но суть такая:

Постфиксный оператор меняет значение и возвращает только временную копию этого значения, которая, как следствие, не может быть изменена. То есть эта копия идёт отдельно от самого значения i, и может быть использована в выражении, но присваивание к i++ не имеет смысла, т.к. результат i++ сохраняется в какой-то другой ячейке памяти, не в той, где лежит i.

Результат ++i записывается в неё же, поэтому выражение ++i = ... имеет смысл (l-value).

ivkremer
  • 2,828
  • 1
    На самом деле ++i должен возвращать значение, и ++i = смысла не иметь
    P.S. - оператор меняет переменную, а возвращает значение
    – timka_s Nov 13 '11 at 09:40
  • 2
    В C++ ++i = x; компилится и работает. Возвращает, но не во временную копию. – ivkremer Nov 13 '11 at 09:42
  • ++i как раз работает по ссылке на переменную, поэтому выполняется выстрее, чем i++, которое возвращает значени. – Стас Литвиненко Nov 13 '11 at 10:09
  • В таком случае, из-за приоритетов операции:

    i = 0; x = (++i + ++i );

    При возврате ссылки, x = 4;
    При возврате значения, x=3;

    – timka_s Nov 13 '11 at 10:14
  • 1

    x = x++; // переменная вообще не меняется

    По-моему тут зависит от компилятора. Может поменяться, а может и нет.

    – devoln Nov 13 '11 at 14:20
  • 1
    Спасибо, только что проверил, в C++ (g++) действительно поменяется. В Java вот только никогда не меняется, я удалил это из ответа. – ivkremer Nov 13 '11 at 14:55
10

Потому что после выполнения выражений:

i = 0;
(1) x = ++i;
(2) x = i++;
В x будут следующие значения:
(1) x = 1;
    i = 1;
(2) x = 0;
    i = 1; 

Все эффекты связаны как раз с таким делением.

То есть ++i означает увеличить i на один и взять его для выражения, а i++ означает взять значение i для выражения и после увеличить i на 1.

αλεχολυτ
  • 28,987
  • 13
  • 60
  • 119
IonRod
  • 497
2

Век живи, век учись. Взял и попробовал обсуждаемые варианты.

#include <stdio.h>
#include <stdlib.h>

main () { int i = 0, x;

x = (++i + ++i); printf ("i = 0; x = (++i + ++i): x=%d i=%d\n",x,i); i = 0; x = (i++ + i++); printf ("i = 0; x = (i++ + i++): x=%d i=%d\n",x,i); x = x++; printf ("x = x++: x=%d i=%d\n",x,i); #ifdef __cplusplus ++i = x; printf ("++i = x: x=%d i=%d\n",x,i); #endif }

c:/Users/avp/src/cc/tst $ gcc t.c c:/Users/avp/src/cc/tst $ ./a i = 0; x = (++i + ++i): x=4 i=2 i = 0; x = (i++ + i++): x=0 i=2 x = x++: x=0 i=2 c:/Users/avp/src/cc/tst $ g++ t.c c:/Users/avp/src/cc/tst $ ./a i = 0; x = (++i + ++i): x=4 i=2 i = 0; x = (i++ + i++): x=0 i=2 x = x++: x=0 i=2 ++i = x: x=0 i=0 c:/Users/avp/src/cc/tst $ g++ --version g++.exe (GCC) 3.4.5 (mingw-vista special r3) Copyright (C) 2004 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

c:/Users/avp/src/cc/tst $ gcc --version gcc.exe (GCC) 3.4.5 (mingw-vista special r3) Copyright (C) 2004 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

c:/Users/avp/src/cc/tst $ c:/Users/avp/src/cc/tst $

avp
  • 46,098
  • 6
  • 48
  • 116
  • простите, что спрашиваю, но вы что использовали shell-код для windows? – sudo97 Nov 14 '11 at 16:07
2

Почему ++i считается lvalue, а i++ rvalue?

Потому что эти операторы так объявлены в соответствии со стандартом:

  1. Прединкремент возвращает ссылку. Ссылке можно присвоить значение, а потому она lvalue («left side value», величина слева от знака равенства).
  2. Постинкремент возвращает значение. Значению ничего присвоить нельзя; его самого присваивают, а потому оно rvalue («right side value», величина справа от знака равенства).
Arhadthedev
  • 11,528
2

Оператор i++ никак не может возвращать lvalue по той простой причине, что нигде не существует подходящего lvalue для возвращения.

Постфиксный оператор ++ обязан возвращать старое значение своего операнда. Однако когда побочные эффекты постфиксного ++ возымели свое действие, его операнд уже поменял свое значение на новое, а старое значения операнда больше нигде в памяти не хранится. Т.е. возвращаемое lvalue просто некуда "привязывать" в памяти. Единственными способами возвращения старого значения является либо запоминание этого старого значения во временном объекте, либо его вычисление "на лету" из нового значения. В обоих случаях старое значение не будет lvalue.

Ситуация с префиксным оператором - обратная. Префиксный оператор должен возвращать новое значение своего операнда. Именно это новое значение операнд и будет содержать после реализации побочных эффектов префиксного ++. Поэтому операнд префиксного оператора как раз и является тем самым lvalue которое оператор сразу и вернет.

-2
i++;                          /*называется постфиксной записью, в которой приоритет
                                операции низкий*/
++i;                          /*называется префиксной записью, в которой
                                приоритет операции высокий*/
/*----------------------------------------------------------------------------------*/
int i = 5;
int x = i++;                  /*здесь x == 5, а i == 6*/
int x = ++i;                  /*здесь x == 6, i == 6*/
sudo97
  • 1,823