Как работают операции Prefix (++x) и Postfix (x++)?

может кто-нибудь сказать мне, как префиксные / постфиксные операторы действительно работают? Я много искал в интернете, но ничего не нашел.

из того, что я могу сказать prefex сначала шагом, затем выполняет операцию и затем присваивает.
Postfix сначала выполнит операцию, затем назначит, а затем увеличит.

но у меня возникли проблемы с моим кодом:

int x, y;
x = 1;
y = x + x++; // (After operation y = 2)(x=2)

однако, когда я делаю:

y = x++ + x; // (After operation y = 3)(x=2)

Я не уверен, почему эти операции будут любой другой. У меня два вопроса:--3-->

  • не могли бы вы объяснить разницу?

  • как это относится к другому префиксу оператора?

7 ответов


  • В C# операнды + это evaulated слева направо.
  • В C и C++ порядок вычисления операндов + это unspecifed.

для C# ваши примеры работают следующим образом:

 y = x + x++;
     ^ x is 1
         ^ x is increased to 2, but the postfix increment returns the old value (1)
 y = 2

 y = x++ + x;
     ^ x becomes 2, but postfix increment returns the old value (1)
           ^ x is now 2 here
 y = 3

этот вопрос задают порядочно. Имейте в виду, что каждый раз, когда кто-то задает этот вопрос, очень много людей публикуют неправильные ответы. Многие люди имеют неверное представление о том, как работают эти операторы, в том числе люди, которые пишут книги по программированию и тем самым учат других людей лжи. Прочитайте другие ответы здесь очень внимательно.

для правильного анализа поведения C# см.:

в чем разница между I++ и ++i?

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

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

посмотреть в вашем конкретном случае:

int x, y;     
x = 1;     
y = x + x++; 

вы сообщаете, что x и y оба 2. Это правильно в C#. В C# правильное поведение:

  • оценить y как переменную
  • оценить x как значение -- это 1
  • оцените x++ как значение. Это оценивает x как переменную, затем принимает ее исходное значение, которое равно 1, затем увеличивает это значение, которое равно 2, затем присваивает 2 x, а затем приводит к исходному значению, которое 1.
  • оцените 1 + 1, что равно 2
  • назначить 2 к y.

таким образом, x и y оба 2 В C#.

C++ может делать то же самое, но разрешено оценивать добавление в порядке справа налево. То есть разрешается делать:

  • оцените x++ как значение. Это оценивает x как переменную, затем принимает ее исходное значение, которое равно 1, затем увеличивает это значение, которое равно 2, затем присваивает 2 x, а затем приводит к исходное значение, которое равно 1.
  • оценить x как значение - это 2
  • оцените 1 + 2, что составляет 3
  • оценить y как переменную
  • назначить 3 к y.

в C++ можно сделать так:

  • оцените x++ как значение. Это оценивает x как переменную, затем принимает ее исходное значение, которое равно 1, затем увеличивает это значение, которое равно 2 ... шаг пропал ... а затем приводит к исходному значению, которое это 1.
  • оценить x как значение -- это 1
  • оцените 1 + 1, что равно 2
  • присваивает 2 x -- шаг, который отсутствовал раньше.
  • оценить y как переменную
  • назначить 2 к y.

таким образом, в C++ вы можете получить y как 3 или 2, в зависимости от прихоти автора компилятора. В C# вы всегда получаете, что y равно 2. В C++ назначение инкремента может произойти в любое время, пока это произойдет. В C#, назначение инкремента должно произойти после вычисляется увеличенное значение и до используется исходное значение. (При наблюдении из исполняющего потока; если вы пытаетесь наблюдать этот материал из другого потока или потоков, все ставки отключены.)

во втором примере:

y = x++ + x; 

в C# требуемое поведение:

  • оценить y как переменную
  • оцените x++ как значение. Этот вычисляет x как переменную, затем принимает ее исходное значение, равное 1, затем увеличивает это значение, равное 2, затем присваивает 2 x и затем приводит к исходному значению, равному 1.
  • оценить x как значение - это 2
  • оцените 1 + 2, что составляет 3
  • назначить 3 к y.

таким образом, правильный ответ в C# заключается в том, что y равно 3, а x равно 2.

опять же, C++ может выполнять эти шаги в любом порядке. C++ разрешено do:

  • оценить x как значение -- это 1
  • оцените x++ как значение. Это вычисляет x как переменную, затем принимает ее исходное значение, которое равно 1, затем увеличивает это значение, которое равно 2, затем присваивает 2 x, а затем приводит к исходному значению, которое равно 1.
  • оцените 1 + 1, что равно 2
  • оценить y как переменную
  • назначить 2 к y.

опять же, в C++ правильный ответ заключается в том, что y равно 2 или 3, в зависимости от прихоти составителя. В C# правильный ответ заключается в том, что y равно 3.


в обоих случаях инкремент был применен после использования X. В первом случае она оценивалась следующим образом:: y = 1 + 1 (с шагом 2)

во втором

y = 1 (с шагом 2) + 2.

вот почему у вас разные ответы.


В C и C++:
Выход нет данных.

Ссылка - Стандарт C++03:

Раздел 5: Выражения, Пункт 4:

за исключением отмеченных [например, специальных правил для && и//], порядок оценки операндов отдельных операторов и подвыражений отдельных выражений, а также порядок, в котором происходят побочные эффекты, Не указан.

В Разделе C99 6.5.

"группировка операторов и операндов указывается синтаксис.72) за исключением случаев, указанных ниже (для вызова функции (), &&, ||, ?:, и операторы запятой), порядок оценки подвыражений и порядок, в котором происходят побочные эффекты, не указаны."


выражения x++ и ++x иметь результат (или значение) и побочный эффект.

если мы ограничимся обсуждением операндов интегрального типа, то результат of x++ независимо от текущего значения x есть. The побочный эффект для увеличения x к 1. Таким образом, учитывая код

x = 0;
y = x++;

результат будет x == 1 и y == 0 (предполагается, что x и y являются интегральными типами).

на ++x на результат равно 1 плюс текущее значение x. The побочный эффект для увеличения x к 1. Таким образом, учитывая код

x = 0;
y = ++x;

результат будет x ==y == 1.

что отличает C и C++ от C#, так это когда вычисляются операнды и когда применяются побочные эффекты. В C# гарантирует, что операнды в выражении всегда вычисляются слева направо. C и c++ гарантируют только оценку слева направо для &&, ||, ?:, запятая, и вызова функции () операторы-для всех других операторов порядок вычисления операндов равен нет данных.

аналогично, в C#, побочные эффекты x++ и ++x будет применяться сразу после оценки выражения, тогда как C и c++ требуют только, чтобы побочный эффект был применен до следующего последовательность точка.

правила оценки C#гарантируют, что выражения, такие как x = x++, a = b++ * b++ и a[i] = i++ хорошо определены, тогда как определения языка C и c++ явно говорят, что такие выражения приводят к undefined поведение (возможен любой результат).


x + x++ и x++ + x являются примером патологических побочных эффектов, от которых вы не хотите зависеть. x++ и ++x оба инкремента x, но при добавлении x порядок оценки не определен-компилятор может выбрать, какую "сторону" он оценивает первым.


считаем:

y = x + x++;

независимо от того, определено ли его поведение или нет (оно не определено в C и C++; по-видимому, оно хорошо определено в C#), что бы вы ни пытались сделать, должен быть лучший способ выразить это.

если вы предполагаете строгую оценку слева направо, то выше может быть написано как:

y = x * 2;
x ++;

смысл ясен и недвусмыслен для любого читателя, который знает, что =, * и ++ mean, и будущие сопровождающие ваш кодекс не будет искушать вас выследить.

или вы могли бы написать x + x или x << 1 если вы не доверяете компилятору для создания эффективного кода, но такое недоверие обычно неуместно.

если вы настаиваете, вы могли бы даже написать:

y = x++ * 2;

это немного кратко для моих личных вкусов, но это все еще недвусмысленно.

если вы хотите понять чей-то код (что, по общему признанию, программисты тратят много времени doing), тогда понимание запутанных выражений может быть важным. Но когда вы пишете свой собственный код, ясность важнее, чем сохранение нажатий клавиш (или демонстрация того, насколько хорошо вы знаете диаграммы приоритета операторов).