Ассоциативность операторов с операторами «постфиксного декремента» и «логического И» в c

Отказ от ответственности: я не пишу код подобным образом, я просто пытаюсь понять, как работает язык c!!!!

Выход 12.

Это выражение (a-- == 10 && a-- == 9) оценивается слева направо, и a по-прежнему равно 10 в a-- == 10, но a равно 9 для a-- == 9.

1) Существует ли четкое правило относительно того, когда оценивается постинкремент? Из этого примера кажется, что он оценивается до &&, но после ==. Это потому, что логический оператор && делает a-- == 10 полным выражением, поэтому a обновляется после его выполнения?

2) Также для c/c++ некоторые операторы, такие как уменьшение префикса, выполняются справа налево, поэтому a == --a сначала уменьшает a до 9, а затем сравнивает 9 == 9. Есть ли причина, по которой c/c++ разработан таким образом? Я знаю, что для Java все наоборот (оценивается слева направо).

#include <stdio.h>

int main() {
    int a = 10;
    if (a-- == 10 && a-- == 9)
        printf("1");
    a = 10;
    if (a == --a)
        printf("2");
    return 0;
}

person rapidDev    schedule 10.10.2018    source источник
comment
Приоритет оператора.   -  person KamilCuk    schedule 10.10.2018
comment
Это не логическое И, это побитовое. Логическое И равно &&   -  person Tormund Giantsbane    schedule 10.10.2018
comment
& - бинарная операция, вы, вероятно, имели в виду &&   -  person Frank    schedule 10.10.2018
comment
Извините, я исправил это. Но тот же вопрос все еще актуален.   -  person rapidDev    schedule 10.10.2018
comment
(a-- == 10 & a-- == 9) зачем вам писать такой код?   -  person Jean-François Fabre    schedule 10.10.2018
comment
Побочные эффекты инкрементов и декрементов завершаются в точках последовательности, а не раньше. Операторы && и || предоставляют точки последовательности; операторы & и | этого не делают.   -  person Jonathan Leffler    schedule 10.10.2018
comment
Почему вы хотите заставить кого-то застрять в том, чтобы прочитать ваш код через это. Это не соревнование по обфускации. Сделайте это очевидным. И это бинарный оператор (&), а не логический (&&)   -  person Jonathan Wood    schedule 10.10.2018
comment
a-- ==10 & a-- == 9? Как вы думаете, какой из них будет вычислен компилятором первым?   -  person Gaurav    schedule 10.10.2018
comment
Обратите внимание, что if (a == --a) — это неопределенное поведение — нет смысла строить догадки о том, что произойдет. Первое условие (теперь) определено; он должен напечатать 1. Вам лучше с новыми строками после печати.   -  person Jonathan Leffler    schedule 10.10.2018
comment
Хороший ответ. @ДжонатанЛеффлер   -  person rapidDev    schedule 10.10.2018
comment
Нет! Этот вопрос не дублирует вопрос о неопределенном поведении. Никакого UB НЕТ, потому что стандарт C говорит об logical and операторе "guarantees left-to-right evaluation; there is a sequence point after the evaluation of the first operand.". Таким образом, все побочные эффекты a-- == 10 завершаются до оценки a-- == 9. Это реактивный другой случай.   -  person ReAl    schedule 10.10.2018
comment
@Observe: ни один из них не компилируется компилятором в первую очередь. В современном компиляторе выражение анализируется и преобразуется во внутреннее представление, которое может быть своего рода структурой расширенных данных. Эта структура данных преобразуется различным кодом в компиляторе, включая оптимизацию, с практически непредсказуемыми результатами. Затем результат используется для генерации некоторого внутреннего кода, который затем используется для генерации ассемблерного кода. Предполагая, что распознаваемые части выражения остаются, любая его часть может быть первой в ассемблерном коде.   -  person Eric Postpischil    schedule 10.10.2018
comment
@EricPostpischil Сообщение См. комментарий: Извините, я исправил. Но тот же вопрос по-прежнему актуален выше. Опечатка в && -› &, но не в логическом слове (см. редактирование 2)   -  person ReAl    schedule 10.10.2018
comment
Извините, я пропустил & в моем описании. Его &&!!!! Это исправлено сейчас   -  person rapidDev    schedule 10.10.2018
comment
Ответ, который я искал, — это точки последовательности. Я просто хотел назвать концепцию, которую я наблюдал в коде.   -  person rapidDev    schedule 10.10.2018


Ответы (3)


Логический оператор && содержит точку последовательности между оценкой первого и второго операнда. Частично это связано с тем, что любой побочный эффект (например, выполняемый оператором --) как часть левой стороны завершается до того, как будет оценена правая сторона.

Это подробно описано в разделе 6.5.13p4 стандарта C. относительно логического оператора И:

В отличие от побитового двоичного оператора &, оператор && гарантирует вычисление слева направо; если оценивается второй операнд, между оценками первого и второго операндов есть точка последовательности. Если первый операнд сравнивается равным 0, второй операнд не оценивается.

В случае этого выражения:

(a-- == 10 && a-- == 9)

Текущее значение a (10) сначала сравнивается на равенство с 10. Это верно, поэтому затем оценивается правая часть, но не раньше побочного эффекта уменьшения a, который был выполнен в левой части. Затем текущее значение a (теперь 9) сравнивается на равенство с 9. Это также верно, поэтому все выражение оценивается как истинное. Перед выполнением следующего оператора выполняется побочный эффект уменьшения a, который был выполнен в правой части.

Однако это выражение:

if (a == --a)

Включает чтение и запись a в одном и том же выражении без точки последовательности. Это вызывает неопределенное поведение.

person dbush    schedule 10.10.2018

Это выражение (a-- == 10 && a-- == 9) вычисляет слева направо,

Да, в основном, но только потому, что && особенный.

и a по-прежнему равно 10 в a-- == 10

Да, потому что a-- возвращает старое значение.

но a равно 9 для a-- == 9.

Да, потому что точка последовательности в && гарантирует, что обновление значения a будет завершено до того, как будет оценена RHS.

1) Существует ли четкое правило относительно того, когда оценивается постинкремент?

Лучший ответ, я думаю, "нет". Побочные эффекты из-за ++ и -- завершаются в какой-то момент до следующей точки последовательности, но дальше этого сказать нельзя. Для четко определенных выражений не имеет значения, когда завершатся побочные эффекты. Если выражение чувствительно к моменту завершения побочного эффекта, это обычно означает, что выражение не определено.

Из этого примера кажется, что он оценивается до &&, но после ==. Это потому, что логический оператор && делает выражение a-- == 10 полным, поэтому a обновляется после его выполнения?

В основном да.

2) Также для c/c++ некоторые операторы, такие как декремент префикса, выполняются справа налево.

Осторожный. Я не уверен, что вы имеете в виду, но что бы это ни было, я почти уверен, что это неправда.

поэтому a == --a сначала уменьшает a до 9, а затем сравнивает 9 == 9.

Нет, a == --a не определено. Невозможно сказать, что он делает.

Есть ли причина, по которой c/С++ разработан таким образом?

Да.

Я знаю, что для Java все наоборот (оценивается слева направо).

Да, Java отличается.


Вот несколько рекомендаций, которые помогут вам понять вычисление выражений C:

  1. Изучите правила приоритета операторов и ассоциативности. Для «простых» выражений эти правила сообщают вам практически все, что вам нужно знать об оценке выражения. Учитывая a + b * c, b умножается на c, а затем произведение добавляется к a из-за более высокого приоритета * над +. Учитывая a + b + c, a добавляется к b, а затем сумма добавляется к c, потому что + ассоциируется слева направо.

  2. За исключением ассоциативности (как указано в пункте 1), старайтесь не использовать слова "слева направо" или "справа налево" вообще. C не имеет ничего похожего на оценку слева направо или справа налево. (Очевидно, что Java отличается.)

  3. Там, где это становится сложно, побочные эффекты. (Когда я сказал «простые» выражения в пункте 1, я в основном имел в виду «выражения без побочных эффектов».) Побочные эффекты включают (а) вызовы функций, (б) присваивания с =, (в) присваивания с +=, -=, и т. д., и, конечно, (d) увеличение/уменьшение с помощью ++ и --. (Если имеет значение, когда вы выбираете из переменной, что обычно имеет место только для переменных, обозначенных как volatile, мы можем добавить (e) выборки из volatile переменных в список.) В общем, вы не можете сказать, когда возникают побочные эффекты. Постарайтесь не заботиться. Пока вам все равно (пока ваша программа нечувствительна к порядку, в котором побочные эффекты имеют значение), это не имеет значения. Но если ваша программа является конфиденциальной, она, вероятно, не определена. (Подробнее см. в пунктах 4 и 5 ниже.)

  4. У вас никогда не должно быть двух побочных эффектов в одном и том же выражении, которые пытаются изменить одну и ту же переменную. (Примеры: i = i++, a++ + a++.) В противном случае выражение не определено.

  5. С одним классом исключений у вас никогда не должно быть побочного эффекта, который пытается изменить переменную, которая также используется в другом месте в том же выражении. (Пример: a == --a.) В противном случае выражение не определено. Исключением является случай, когда полученное значение используется для вычисления сохраняемого значения, как в i = i + 1.

person Steve Summit    schedule 10.10.2018

С «логическим и» оператор (a-- == 10 && a-- == 9) является правильно сформированным выражением (без неопределенного поведения, как в a++ + a++).

Стандарт C говорит о «логическом и»/«логическом или» операторах:

гарантирует оценку слева направо; есть точка последовательности после оценки первого операнда.

Таким образом, все побочные эффекты первого подвыражения a-- == 10 завершаются перед вычислением второго подвыражения a-- == 9. a равно 9 перед вычислением второго подвыражения.

person ReAl    schedule 10.10.2018
comment
На данный момент это относится только к первому из двух операторов if (но делает это правильно, учитывая отредактированный вопрос). - person Jonathan Leffler; 12.10.2018
comment
Согласитесь, во втором if порядок вычисления == операндов не определен, так что результат оператора тоже не определен. - person ReAl; 12.10.2018