Если скобки имеют более высокий приоритет, то почему оператор инкремента решается первым?

у меня есть одна строка кода

int a = 10;
a = ++a * ( ++a + 5);

мой ожидаемый результат был 12 * (11 + 5) = 192 ,а я 187. Насколько я знал оператор инкремента внутри () сначала нужно решить, тогда почему сначала решается тот, что снаружи?

5 ответов


выражения вычисляются слева направо. Скобки (и приоритет) просто выражают группировку, они не выражают порядок оценки.

Так

 11 * (12 + 5)
++a   ++a

равна

187

цитирую блог Эрика Липперта:

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

приоритет правила описывают, как выражение underparenthesized должно быть заключено в скобки, когда выражение смешивает различные типы операторов.

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

порядок оценки правила описывают порядок, в котором вычисляется каждый операнд в выражении.

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


обновление:

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

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

вызовет неопределенное поведение. Да, он будет вызывать UB, если нет гарантии порядка оценки операндов оператора.

но это неверно в контексте языка программирования java. Он имеет хорошо определенное поведение в java (а также В C#). Спецификация языка Java гласит, что:

15.7. Порядок Оценки

язык программирования Java гарантирует, что операнды операторов представляется оценена в определенном порядке оценки, а именно, слева направо.

пример 15.7.1-1. Сначала Вычисляется Левый Операнд

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

class Test1 {
    public static void main(String[] args) {
        int i = 2;
        int j = (i=3) * i;
        System.out.println(j);
    }
}

эта программа производит вывод:

9

это не допускается для оценки * оператор для производства 6 вместо 9.

но, все же спецификация java четко заявляет, что не писать такие коды:

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


из фрагмента

int a = 10;
a = ++a * ( ++a + 5);

иногда самым простым решением является использование javap для понимания порядка оценки:

 0: bipush 10 // push 10 onto the stack (stack={10})
 2: istore_1  // store 10 into variable 1 (a = 10, stack={})
 3: iinc 1, 1 // increment local variable 1 by 1 (a = 11, stack={})
 6: iload_1   // push integer in local variable 1 onto the stack (a = 11, stack = {11})
 7: iinc 1, 1 // increment local variable 1 by 1 (a = 12, stack={11})
 10: iload_1  // push integer in local variable 1 onto the stack (a = 12, stack = {12, 11})
 11: iconst_5 // load the int value 5 onto the stack (a = 12, stack = {5, 12, 11})
 12: iadd     // add 5 and 12 (a = 12, stack = {17, 11})
 13: imul     // multiply 17 and 11 (a = 12, stack = {})
  1. a увеличивается на 1. (строка 3) / / a = 11
  2. a увеличивается на 1. (строка 7) / / a = 12
  3. добавить 5 до a (строка 12) / / a = 17
  4. умножение 11 to 17 (строка 13) / / a = 187

(10 + 1 + 1 + 5) * 11 = 187


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

(a()+b()) * (c()+d())
a() + (b()*c()) + d()

скобки не должны (и в Java не могут) влиять на порядок, в котором вызываются a (), b (), c () и d (). Они могут повлиять на умножение выполняется до или после вызова d (), но только в очень редких случаях (например, D (), вызывающий собственный интерфейс Java, который изменяет режим численного округления, используемый в умножении таким образом, о котором Java не знает), код будет иметь какой-либо способ узнать или заботиться о том, было ли умножение выполнено до или после d().

в противном случае важно то, что в первом случае одна операция сложения будет действовать на a () и b (), а другая-на c () и d (); умножение будет действовать на a ()+b () и c ()+d (). В другом случае первое умножение будет действовать на b() и c(), первое сложение-на a() и вышеупомянутое произведение, а второе сложение-на первую сумму и d().


  int a = 10;
  a = ++a * ( ++a + 5);

выше вид выражений всегда оценивается слева направо, будь то C или JAVA

в этом случае он решается следующим образом 11*(12+5), что приводит к 11 * 17 = 187 / / w.r.t java

но если это мы решаем то же выражение w.r.t C-язык

затем ответ изменяется по мере изменения способа оценки

в c происходит первый предварительный инкремент/предварительный декремент, поэтому, если "N" не раз pre inc/dec есть ли в выражении тогда inc / dec произойдет сначала "N" no of times

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

i.e A увеличивается до 11, затем снова 12, поскольку в выражении есть два увеличения для a, а затем выражение оценивается как

12*(12+5)=12*17=204 //Вт.Р.Т C-язык