Подтвердить что ты не робот

Почему этот код имеет значение true?

int main() {
    int a = 1;
    int b = 0;

    if (a = b || ++a == 2)
        printf("T: a=%i, b=%i", a, b);
    else
        printf("F: a=%i, b=%i", a, b);

    return 0;
}

Посмотрим на этот простой фрагмент кода. Результат: T: a = 1, b = 0

Почему? (примечание a=b использует операнд назначения, а не сравнение)

Я понимаю, что ноль присваивается a, тогда a увеличивается на 1. 1 не равно 2. Поэтому результат должен быть a = 1, b = 0. Но почему это условие оценивается как истинное? Ни один из (a=b) или (++a == 2) не верен... Что я пропустил?

Вот еще одна короткая программа, которая печатает F, как ожидалось:

int main() {
    int a = 1;
    int b = 0;

    if (a = b) printf("T"); else printf("F");

    return 0;
}
4b9b3361

Ответ 1

Вы путаете себя с вводящим в заблуждение промежутком.

if (a = b || ++a == 2)

совпадает с:

if (a = (b || ((++a) == 2)))

На самом деле это поведение undefined. Хотя между оценкой b и оценкой ((++a) == 2) существует точка последовательности, не существует точки последовательности между подразумеваемым присваиванием на a, а другая записывается в a из-за явного назначения =.

Ответ 2

На самом деле назначение имеет самый низкий приоритет оператора, поэтому ваш оператор if эквивалентен:

if ( a = ( b || ( ++a == 2 ) ) )

Итак, вы назначаете a 1, а также увеличиваете его в одном выражении. Я думаю, что это приводит к поведению undefined, но конечным результатом является то, что a - 1 в вашем компиляторе.

Ответ 3

Если вы используете GCC или другой компилятор с подобными полезными предупреждениями, включение предупреждений даст вам очень большой намек на то, что здесь не так. С помощью gcc -Wall:

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

Чтобы быть точным: компилятор интерпретирует код как if (a = (b || ++a == 2)), и предупреждение указывает на то, что вы пишете его как if ((a = (b || ++a == 2))), чтобы подчеркнуть, что код предназначен, а не опечатка для более распространенного if (a == (b || ++a == 2)).

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