Ответ 1
Выражения оцениваются слева направо. Скобки (и приоритет) просто выражают группировку, они не выражают порядок оценки.
Итак,
11 * (12 + 5)
++a ++a
равно
187
У меня есть один код строки,
int a = 10;
a = ++a * ( ++a + 5);
Мой ожидаемый результат был 12 * (11 + 5) = 192
, но я получил 187. Насколько я знал, что оператор инкремента внутри ()
должен быть решен первым, то почему первая проблема сначала решена?
Выражения оцениваются слева направо. Скобки (и приоритет) просто выражают группировку, они не выражают порядок оценки.
Итак,
11 * (12 + 5)
++a ++a
равно
187
Цитата из Блог Эрика Липперта:
Оценка арифметического выражения контролируется тремя наборами правил: правилами приоритета, правилами ассоциативности и правилами порядка.
Приоритет правила описывают, как должно быть заключено нижнее сжатое выражение, когда выражение смешивает различные типы операторов.
ПравилаAssociativity описывают, как заключать в нижеследующее выражение выражение, когда выражение имеет связку одного и того же оператора.
Правила оценки описывают порядок, в котором оценивается каждый операнд в выражении.
Более высокий приоритет приводит к группировке операндов с оператором и не означает оценку операндов. Это порядок оценки, который определяет последовательность оценки подвыражений в выражении.
Как я вижу, многие программисты считают, что утверждение
a = ++a * ( ++a + 5);
вызывается поведение undefined. Да, он будет вызывать UB, если нет гарантии порядка оценки операндов оператора.
Но это не так в контексте языка Java-программирования. Он имеет четко определенное поведение в java (а также в С#). Спецификация языка Java гласит, что:
Язык программирования 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 = {})
a
увеличивается на 1. (строка 3)//a = 11a
увеличивается на 1. (строка 7)//a = 125
в a
(строка 12)//a = 1711
на 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 Native, который изменяет режим численного округления, используемый в умножении, способом, которым Java не знает about) будет иметь какой-либо способ узнать или заботиться о том, выполнялось ли умножение до или после d().
В противном случае важно то, что в первом случае одна операция сложения будет действовать на() и b(), а другая - на c() и d(); умножение будет действовать на() + 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-language
тогда ответ изменяется как способ изменения оценки
в c происходит первый предварительный приращение/пре-декремент, поэтому, если "N" нет времени pre inc/dec есть ли в выражении, тогда inc/dec произойдет сначала "N" no times
то одно и то же значение будет заменено в каждом вступлении переменной в выражении и вычисляется значение выражения и после этого происходит приращение/декремент post
i. a получает приращение до 11, затем снова 12, поскольку в выражении есть два приращения для a, а затем выражение оценивается как
12 * (12 + 5) = 12 * 17 = 204//w.r.t C-language