Когда ++ не дает такие же результаты, как +1? - PullRequest
27 голосов
/ 22 сентября 2008

Следующие два фрагмента кода C # дают разные результаты (при условии, что переменный уровень используется как до, так и после рекурсивного вызова). Почему?

public DoStuff(int level)
{
  // ...
  DoStuff(level++);
  // ...
}

public DoStuff(int level)
{
  // ...
  DoStuff(level+1);
  // ...
}

Прочитав некоторые ответы ниже, я подумал, что было бы целесообразно опубликовать трассировки стека для уровней ++, ++ level и level + 1, чтобы подчеркнуть, насколько обманчива эта проблема.

Я упростил их для этого поста. Рекурсивная последовательность вызовов начинается с DoStuff (1).

// уровень ++

DoStuff(int level = 1)
DoStuff(int level = 2)
DoStuff(int level = 2)
DoStuff(int level = 2)

// ++ level

DoStuff(int level = 4)
DoStuff(int level = 4)
DoStuff(int level = 3)
DoStuff(int level = 2)

// уровень + 1

DoStuff(int level = 4)
DoStuff(int level = 3)
DoStuff(int level = 2)
DoStuff(int level = 1)

Ответы [ 15 ]

44 голосов
/ 22 сентября 2008

Чтобы уточнить все остальные ответы:

+++++++++++++++++++++

DoStuff(a++);

Эквивалентно:

DoStuff(a);
a = a + 1;

+++++++++++++++++++++

DoStuff(++a);

Эквивалентно:

a = a + 1;
DoStuff(a);

+++++++++++++++++++++

DoStuff(a + 1);

Эквивалентно:

b = a + 1;
DoStuff(b);

+++++++++++++++++++++

29 голосов
/ 22 сентября 2008

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

public DoStuff(int level)
{  
  // ...
  int temp = level;
  level = level + 1;
  DoStuff(temp);
  // ...
}

Обратите внимание, что вы также можете написать ++ уровень; это будет эквивалентно:

public DoStuff(int level)
{  
  // ...
  level = level + 1;
  DoStuff(level);
  // ...
}

На мой взгляд, лучше не злоупотреблять операторами ++ и -; это быстро запутывает и / или не определяет, что на самом деле происходит, и современные компиляторы C ++ в любом случае не генерируют более эффективный код с этими операторами.

27 голосов
/ 22 сентября 2008

уровень ++ передаст уровень в DoStuff и затем увеличит уровень для использования в оставшейся части функции. Это может быть довольно неприятной ошибкой, поскольку рекурсия никогда не закончится (из того, что показано DoStuff всегда передается одно и то же значение). Возможно, вместо этого подразумевается ++ level , поскольку это противоположно level ++ (увеличивает уровень и передает увеличенное значение в DoStuff )?

level + 1 передаст level + 1 в DoStuff и оставит level неизменным для остальной части функции.

12 голосов
/ 22 сентября 2008

Возвращаемое значение level++ будет level и therefore передать level в DoStuff. Это может быть довольно неприятной ошибкой, поскольку рекурсия никогда не закончится (из того, что показано, DoStuff всегда передается с одним и тем же значением). Возможно, вместо этого подразумевается ++level или level + 1?

level + 1 передаст level + 1 в DoStuff и оставит level без изменений для остальной части функции.


Оператор постинкремента (переменная ++) точно эквивалентен функции

int post_increment(ref int value)
{
    int temp = value;
    value = value + 1
    return temp;
}

, в то время как оператор предварительного увеличения (переменная ++) точно эквивалентен функции

int pre_increment(ref int value)
{
    value = value + 1;
    return value;
}

Следовательно, если вы развернете оператор inline в код, операторы будут эквивалентны:

DoStuff(a + 1)

int temp = a + 1;
DoStuff(temp);

DoStuff(++a)

a = a + 1;
DoStuff(a);

DoStuff(a++);

int temp = a;
a = a + 1;
DoStuff(temp);

Важно отметить, что постинкремент не эквивалентен:

DoStuff(a);
a = a + 1;

Кроме того, в качестве точки стиля нельзя увеличивать значение, если только не предполагается использовать увеличенное значение (конкретная версия правила ", не присваивайте значение переменной, если вы не планируете использовать это значение "). Если значение i + 1 никогда больше не используется, тогда предпочтительное использование должно быть DoStuff(i + 1), а не DoStuff(++i).

2 голосов
/ 22 сентября 2008

Первый использует значение в уровне, а затем увеличивает его.

Последний использует уровень + 1 в качестве переданной переменной.

1 голос
/ 22 сентября 2008

На уровне ++ вы используете постфиксный оператор. Этот оператор работает после использования переменной. То есть после того, как он помещен в стек для вызываемой функции, он увеличивается. С другой стороны, уровень + 1 - это простое математическое выражение, которое оценивается и результат передается вызываемой функции. Если вы хотите сначала увеличить переменную, а затем передать ее вызываемой функции, вы можете использовать префиксный оператор: ++ level

1 голос
/ 22 сентября 2008
public DoStuff(int level)
{

  // DoStuff(level);
  DoStuff(level++);
  // level = level + 1;
  // here, level's value is 1 greater than when it came in
}

Фактически увеличивает значение уровня.

public DoStuff(int level)
{
  // int iTmp = level + 1;
  // DoStuff(iTmp);
  DoStuff(level+1);
  // here, level's value hasn't changed
}

на самом деле не увеличивает значение уровня.

Не большая проблема до вызова функции, но после вызова функции значения будут другими.

1 голос
/ 22 сентября 2008

level++ возвращает текущее значение level, затем увеличивается level. level+1 вообще не меняет level, но DoStuff вызывается со значением (level + 1).

0 голосов
/ 15 сентября 2012

Проще говоря, ++var является префиксным оператором и будет увеличивать переменные до , а остальная часть выражения вычисляется. var++, постфиксный оператор, увеличивает переменную после , остальная часть выражения вычисляется. И, как уже упоминали другие, конечно, var+1 создает только временную переменную (отдельную в памяти), которая инициируется с помощью var и увеличивается с постоянной 1.

0 голосов
/ 22 сентября 2008

Когда вы используете язык, который допускает перегрузку операторов, и '+ ' был определен для выполнения чего-то другого, кроме пост- и префикса '++'.

Опять же, я видел такие мерзости только в школьных проектах *, если вы сталкиваетесь с этим в дикой природе, у вас, вероятно, есть действительно хорошая, хорошо документированная причина.

[* стек целых чисел, если я не ошибаюсь. «++» и «-» нажали и вытолкнули, а «+» и «-» выполнили обычную арифметику]

...