Запустите следующий код:
// In Java, output #####
public static void main(String[] args) {
int i = 1;
if (i == (i = 2)) {
System.out.println("@@@@@");
} else {
System.out.println("#####");
}
}
Но:
// In C, output @@@@@,I did test on Clion(GCC 7.3) and Visual Studio 2017
int main(int argc, char *argv[]) {
int i = 1;
if (i == (i = 2)) {
printf("@@@@@");
} else {
printf("#####");
}
return 0;
}
Мотивация для того, чтобы задать этот вопрос, исходит из следующего кода:
// The code is from the JDK 11 - java.util.concurrent.atomic.AtomicInteger
// I am curious about the behavior of the variable prev.
public final int getAndUpdate(IntUnaryOperator updateFunction) {
int prev = get(), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = updateFunction.applyAsInt(prev);
if (weakCompareAndSetVolatile(prev, next))
return prev;
haveNext = (prev == (prev = get()));
}
}
Итак, как объяснить два вышеупомянутых разных режима выполнения?
Результат: запутанный код. Лучше не имитировать это, если вы не участвуете в конкурсе по обфускации java.
Возможный дубликат Логические различия в C и Java
(хотя конкретный используемый оператор (==, ++, = и т. д.) отличается, ответ (он хорошо определен в Java, точка последовательности -> поведение undefined) тот же)
Включите предупреждения компилятора, которые бы ответили за вас на ваш вопрос, особенно если бы вы попробовали clang. warning: unsequenced modification and access to 'i' [-Wunsequenced]. godbolt.org/z/UtGRVO
@theg Почему их "мотивация" имеет значение? Ответы в обоих списках идентичны, не так ли?
@TheGreatDuck После вашего редактирования вопрос все еще в силе и больше не является дубликатом, но Ответ, получивший наибольшее количество голосов, содержит много несвязанных частей (и выражение «два режима выполнения» больше не имеет смысла).
@TheGreatDuck Пожалуйста, не редактируйте вопрос таким образом, чтобы изменить то, что задает OP, особенно если есть высоко оцененные ответы на исходный вопрос. Если вы чувствуете, что вопрос не верный / правильный, проголосуйте / проголосуйте против и двигайтесь дальше.
@TheGreatDuck Посмотреть историю изменений.
Кстати, есть ли причина не принимать ответ? : D
@AnttiHaapala Только что обнаружил функцию "принять" ...




поведение программы C, которая выполняет выражение i == (i = 2), - это неопределенный.
Это происходит от C11 6.5p22:
- If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.84)
i в левой части == представляет собой вычисление значения для значения скалярного объекта i, а i = 2 в правой части имеет побочный эффект присвоения значения 2i. LHS и RHS == не имеют последовательности относительно. друг с другом. Следовательно, вся программа в C.
Скомпилируйте с gcc -Wall и GCC выдаст:
unsequenced.c:5:16: warning: operation on ‘i’ may be undefined [-Wsequence-point]
if (i == (i = 2)) {
~~~^~~~
В отличие от C, Java гарантирует порядок оценки для операндов (слева направо), поэтому
haveNext = (prev == (prev = get()));
правильно в Java. Значение LHS определяется строго до оценки побочного действия на RHS.
В C вы имеют, чтобы написать это как-то вроде
newPrev = get();
haveNext = (prev == newPrev);
prev = newPrev;
Почему i на LHS - это вычисление значений? Это где-то указано в Стандарте?
@SomeName конечно же где-то указано в стандарте. : D
Так как насчет ссылки? :) Единственное, что я смог найти, это термин value, определенный в 3.19, но никакого определения value computation там не было.
@SomeName вам нужно вывести его из слишком большого количества мест в прозе: / сноска 84, однако, может использоваться как ярлык. Если бы i не имел значения выражения, тогда определялся бы a[i++] = i;. Однако сноски не являются нормативными.
@SomeName 5.1.2.3p2 говорит «вычисление значения выражения lvalue», что в данном случае имеет место, i - это lvalue, и его значение необходимо вычислить, поскольку это не операнд. 6.3.2.1p2 говорит «преобразовано в значение, хранящееся в указанном объекте», но это то, что упоминается в 5.1.2.3p2
Очень полезно. Спасибо.
* это операнд
Интересно, что clang решает оценивать в порядке, обратном gcc, и постоянное распространение дает нам #### вместо @@@@. godbolt.org/z/UtGRVO (и его предупреждение более наглядно: warning: unsequenced modification and access to 'i' [-Wunsequenced], и включено даже без -Wall.)
Интуитивно кажется, что RHS можно вычислить, только вернув i после присвоения ему (то есть int& operator=(...)), поэтому должна быть неявная точка последовательности или что-то в этом роде. Вместо этого следует думать о RHS как о i и о сравнительном ==, упорядочивающем undefined w.r.t. присвоение =?
@imallett, но это C, а не C++
@AnttiHaapala И i, будучи int, в любом случае не будет иметь определяемого пользователем оператора. Дело в грамматике: конструкция foo=bar возвращает foo& (даже если ссылки не представлены в C).
@imallett неверно, в C присвоение имеет значение выражения, это определенно нет lvalue.
В C поведение i == (i = 2) не определено, поскольку он пытается как обновить объект, так и использовать значение этого объекта в вычислениях без промежуточной точки последовательности. Результат будет зависеть от компилятора, настроек компилятора и даже от окружающего кода.
Спецификация языка Java (§15.7) сообщает:
The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.
спецификация (§15.21.1) также заявляет, что:
The value produced by the
==operator istrueif the value of the left-hand operand is equal to the value of the right-hand operand; otherwise, the result isfalse.
Поэтому в Java оператор if во время выполнения будет выглядеть следующим образом, что, очевидно, оценивается как false:
if (1 == 2) {
}
В C он просто не определен (см. Ответ Антти).
Один объясняет два разных режима выполнения, сначала отмечая, что это два совершенно разных языка. У них общий синтаксис немного, но на этом сходство заканчивается.