a = (a++) * (a++) дает странные результаты в Java [закрыто]

Я учусь на экзамен OCPJP, и поэтому я должен понять каждую маленькую странную деталь Java. Это включает порядок, в котором операторы pre - и post-increment применяются к переменным. Следующий код дает мне странные результаты:

int a = 3;

a = (a++) * (a++);

System.out.println(a); // 12

разве ответ не должен быть 11? Или, может, 13? Но не 12!

ПОСЛЕДУЮЩИЕ ДЕЙСТВИЯ:

каков результат следующего кода?

int a = 3;

a += (a++) * (a++);

System.out.println(a);

15 ответов


после первый a++ a будет 4. Так у вас есть 3 * 4 = 12.

(a становится 5 после 2-го a++, но это отбрасывается, потому что задание a = переопределяет его)


ваше утверждение:

a += (a++) * (a++);

эквивалентно любому из них:

a = a*a + 2*a
a = a*(a+2)
a += a*(a+1)

вместо этого используйте любой из них.


a++ означает "значение a, и a затем увеличивается на 1". Поэтому, когда вы бежите

(a++) * (a++)

первый a++ сначала вычисляется и выдает значение 3. a затем увеличивается на 1. Второй a++ затем вычисляется. a производит значение 4, а затем снова увеличивается (но теперь это не имеет значения)

так это превращается в

a = 3 * 4

что равно 12.


int a = 3;
a += (a++) * (a++);

сначала создайте дерево синтаксиса:

+=
  a
  *
    a++
    a++

чтобы оценить его, начните с внешнего элемента most и спуска рекурсивно. Для каждого элемента сделайте:

  • оценивать детей слева направо
  • оценить сам элемент

на += оператор особенный: он расширяется до чего-то вроде left = left + right, но только вычисление выражения left раз. Тем не менее, левая сторона оценивается к значению (а не только переменной), прежде чем правая сторона будет оценена в значение.

это приводит к:

  1. запустить оценке +=
  2. оценить левую сторону присвоения переменной a.
  3. оцените переменную a стоимостью 3, который будет использоваться в дополнение.
  4. запустить оценке *
  5. оценить первый a++. Это возвращает текущее значение 3 и устанавливает a to 4
  6. оценить второй a++. Это возвращает текущее значение 4 и телеаппаратуры a to 5
  7. рассчитать продукт: 3*4 = 12
  8. выполнить +=. Левая сторона была оценена на 3 на третьем шаге и правой стороне 12. Таким образом, он присваивает 3+12=15 a.
  9. конечное значение a - это 15.

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


(a++) является шагом post, поэтому значение выражения равно 3.

(a++) является шагом post, поэтому значение выражения теперь равно 4.

оценка выражения происходит слева направо.

3 * 4 = 12 

каждый раз, когда вы используете a++, вы постинкрементируете a. Это означает, что первый a++ оценивается в 3, а второй - в 4. 3 * 4 = 12.


существует общее отсутствие понимания того, как работают операторы. Честно говоря, каждый оператор-это синтаксический сахар.

все, что вам нужно сделать, это понять, что на самом деле происходит за каждым оператором. Предположим следующее:

a = b -> Operators.set(a, b) //don't forget this returns b
a + b -> Operators.add(a, b)
a - b -> Operators.subtract(a, b)
a * b -> Operators.multiply(a, b)
a / b -> Operators.divide(a, b)

составные операторы могут быть переписаны с использованием этих обобщений (Пожалуйста, игнорируйте возвращаемые типы для простоты):

Operators.addTo(a, b) { //a += b
  return Operators.set(a, Operators.add(a, b));
}

Operators.preIncrement(a) { //++a
  return Operators.addTo(a, 1);
}

Operators.postIncrement(a) { //a++
  Operators.set(b, a);
  Operators.addTo(a, 1);
  return b;
}

вы можете переписать пример:

int a = 3;
a = (a++) * (a++);

as

Operators.set(a, 3)
Operators.set(a, Operators.multiply(Operators.postIncrement(a), Operators.postIncrement(a)));

который можно разделить, используя несколько переменных:

Operators.set(a, 3)
Operators.set(b, Operators.postIncrement(a))
Operators.set(c, Operators.postIncrement(a))
Operators.set(a, Operators.multiply(b, c))

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


в случае :

int a = 3;  
a = (a++) * (a++); 

a = 3 * a++; now a is 4 because of post increment
a = 3 * 4; now a is 5 because of second post increment
a = 12; value of 5 is overwritten with 3*4 i.e. 12 

следовательно, мы получаем выход как 12.

в случае :

a += (a++) * (a++); 
a = a + (a++) * (a++);
a = 3 + (a++) * (a++); // a is 3
a = 3 + 3 * (a++); //a is 4
a = 3 + 3 * 4; //a is 5
a = 15

главное отметить, что в этом случае компилятор решает слева направо и в случае post increment, значение перед инкрементом используется в расчете и при движении слева направо используется инкрементное значение.


(a++) означает возвращение a и инкремент, так (a++) * (a++) означает 3 * 4


вот код java:

int a = 3;
a = (a++)*(a++);

вот код:

  0  iconst_3
  1  istore_1 [a]
  2  iload_1 [a]
  3  iinc 1 1 [a]
  6  iload_1 [a]
  7  iinc 1 1 [a]
 10  imul
 11  istore_1 [a]

вот что происходит:

толкает 3 в стек, затем выскакивает 3 из стека и сохраняет его в a. Теперь a = 3 и стек пуст.

  0  iconst_3
  1  istore_1 a

теперь он выталкивает значение из " a " (3) в стек, а затем увеличивает a (3 -> 4).

  2  iload_1 [a]
  3  iinc 1 1 [a]

Итак, теперь " a "равно " 4", стек равен {3}.

затем он снова загружает " a "(4), толкает в стек и инкременты "a".

  6  iload_1 [a]
  7  iinc 1 1 [a]

теперь " a " равно 5, а стек равен {4,3}

таким образом, он, наконец, всплывает два значения fisrt из стека (4 и 3), умножает и сохраняет его обратно в стек (12).

 10  imul

теперь " a " равно 5, а стек равен 12.

наконец, это pops 12 из стека и магазины на.

 11  istore_1 [a]

Тада!


Это 12. Выражение начинает оценивать слева. Так оно и есть:

a = (3++) * (4++);

Как только первая часть (3++) оценивается, a равно 4, поэтому в следующей части он делает a = 3*4 = 12. Обратите внимание, что последний post-increment (4++) выполняется, но не имеет эффекта, так как после этого a присваивается значение 12.


Пример 1

int a = 3;

a = (++a) * (a++);

System.out.println(a); // 16

Пример 2

int a = 3;

a = (++a) * (++a);

System.out.println(a); // 20

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


все ясно объяснили первое выражение, и почему значение a равно 12.

для следующего вопроса ответ совершенно очевиден случайному наблюдателю:

17


приращения до и после префикса имеют более высокий приоритет, чем оператор умножения. отсюда и выражение оценивается как 3*4.


Если вы используете a++ в следующий раз, когда вы используете a, он увеличивается на единицу. Так что твои дела

a = 3 * (3 + 1) = 3 * 4 = 12