Каков результат i == (i = 2)?

Запустите следующий код:

// 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()));
    }
}

Итак, как объяснить два вышеупомянутых разных режима выполнения?

Один объясняет два разных режима выполнения, сначала отмечая, что это два совершенно разных языка. У них общий синтаксис немного, но на этом сходство заканчивается.

StoryTeller - Unslander Monica 02.12.2018 07:44

Результат: запутанный код. Лучше не имитировать это, если вы не участвуете в конкурсе по обфускации java.

Tristan 02.12.2018 08:29

Возможный дубликат Логические различия в C и Java

user202729 02.12.2018 09:16

(хотя конкретный используемый оператор (==, ++, = и т. д.) отличается, ответ (он хорошо определен в Java, точка последовательности -> поведение undefined) тот же)

user202729 02.12.2018 09:17

Включите предупреждения компилятора, которые бы ответили за вас на ваш вопрос, особенно если бы вы попробовали clang. warning: unsequenced modification and access to 'i' [-Wunsequenced]. godbolt.org/z/UtGRVO

Peter Cordes 02.12.2018 17:44

@theg Почему их "мотивация" имеет значение? Ответы в обоих списках идентичны, не так ли?

user202729 03.12.2018 06:35

@TheGreatDuck После вашего редактирования вопрос все еще в силе и больше не является дубликатом, но Ответ, получивший наибольшее количество голосов, содержит много несвязанных частей (и выражение «два режима выполнения» больше не имеет смысла).

user202729 04.12.2018 11:06

@TheGreatDuck Пожалуйста, не редактируйте вопрос таким образом, чтобы изменить то, что задает OP, особенно если есть высоко оцененные ответы на исходный вопрос. Если вы чувствуете, что вопрос не верный / правильный, проголосуйте / проголосуйте против и двигайтесь дальше.

Sneftel 04.12.2018 11:19

@TheGreatDuck Посмотреть историю изменений.

user202729 05.12.2018 10:49

Кстати, есть ли причина не принимать ответ? : D

Antti Haapala 17.12.2018 13:57

@AnttiHaapala Только что обнаружил функцию "принять" ...

kangjianwei 17.12.2018 16:04
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
44
12
3 246
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

поведение программы C, которая выполняет выражение i == (i = 2), - это неопределенный.

Это происходит от C11 6.5p22:

  1. 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 - это вычисление значений? Это где-то указано в Стандарте?

Some Name 02.12.2018 07:11

@SomeName конечно же где-то указано в стандарте. : D

Antti Haapala 02.12.2018 07:11

Так как насчет ссылки? :) Единственное, что я смог найти, это термин value, определенный в 3.19, но никакого определения value computation там не было.

Some Name 02.12.2018 07:13

@SomeName вам нужно вывести его из слишком большого количества мест в прозе: / сноска 84, однако, может использоваться как ярлык. Если бы i не имел значения выражения, тогда определялся бы a[i++] = i;. Однако сноски не являются нормативными.

Antti Haapala 02.12.2018 07:34

@SomeName 5.1.2.3p2 говорит «вычисление значения выражения lvalue», что в данном случае имеет место, i - это lvalue, и его значение необходимо вычислить, поскольку это не операнд. 6.3.2.1p2 говорит «преобразовано в значение, хранящееся в указанном объекте», но это то, что упоминается в 5.1.2.3p2

Antti Haapala 02.12.2018 07:39

Очень полезно. Спасибо.

Some Name 02.12.2018 08:29

* это операнд

Antti Haapala 02.12.2018 09:29

Интересно, что clang решает оценивать в порядке, обратном gcc, и постоянное распространение дает нам #### вместо @@@@. godbolt.org/z/UtGRVO (и его предупреждение более наглядно: warning: unsequenced modification and access to 'i' [-Wunsequenced], и включено даже без -Wall.)

Peter Cordes 02.12.2018 17:41

Интуитивно кажется, что RHS можно вычислить, только вернув i после присвоения ему (то есть int& operator=(...)), поэтому должна быть неявная точка последовательности или что-то в этом роде. Вместо этого следует думать о RHS как о i и о сравнительном ==, упорядочивающем undefined w.r.t. присвоение =?

imallett 02.12.2018 20:42

@imallett, но это C, а не C++

Antti Haapala 03.12.2018 13:24

@AnttiHaapala И i, будучи int, в любом случае не будет иметь определяемого пользователем оператора. Дело в грамматике: конструкция foo=bar возвращает foo& (даже если ссылки не представлены в C).

imallett 03.12.2018 15:16

@imallett неверно, в C присвоение имеет значение выражения, это определенно нет lvalue.

Antti Haapala 20.12.2018 09:57

В 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 is true if the value of the left-hand operand is equal to the value of the right-hand operand; otherwise, the result is false.

Поэтому в Java оператор if во время выполнения будет выглядеть следующим образом, что, очевидно, оценивается как false:

if (1 == 2) {

}

В C он просто не определен (см. Ответ Антти).

Другие вопросы по теме