+ (+ k--) выражение в C

9

Я видел этот вопрос в тесте, в котором мы должны сообщить вывод следующего кода.

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
    k=k++;
    printf("%d\n", k);  
    return 0;
}

Выход есть -1. Я не уверен, почему это ответ, хотя.

Что означает выражение +(+k--)в C?

Анкур Гаутам
источник
4
Это было отформатировано так? Это значит ;-)
Питер - Восстановить Монику
3
Подсказка: никогда не пишите такой код BS в реальной жизни.
Jabberwocky
5
Это не UB. Смотри мой ответ.
dbush
@ Peter-ReinstateMonica Да, это было отформатировано так.
Анкур Гаутам
2
Из-за его надуманного характера и причудливых поворотов - k=k++не определено, но не определено, потому что оно никогда не выполняется из-за запутанного состояния - я голосую за закрытие, как дубликат этого вопроса .
Стив Саммит

Ответы:

10

[Для протокола, я отредактировал этот ответ довольно значительно, так как он был принят и проголосовал. Это все еще говорит в основном то же самое, все же.]

Этот код глубоко, возможно, намеренно, запутывает. Он содержит узко предотвращенный экземпляр ужасного неопределенного поведения . В принципе невозможно определить, был ли человек, который создал этот вопрос, очень, очень умным или очень, очень глупым. И «урок», который может принести этот код для изучения или опроса о вас, а именно, о том, что унарный оператор плюс мало что делает, - безусловно, не является достаточно важным, чтобы заслужить такого рода диверсионное неверное направление.

Есть два запутанных аспекта кода, странное условие:

while(+(+k--)!=0)

и безумное заявление, которое оно контролирует:

k=k++;

Я собираюсь осветить вторую часть первой.

Если у вас есть такая переменная, kкоторую вы хотите увеличить на 1, C дает вам не один, не два, не три, а четыре различных способа сделать это:

  1. k = k + 1
  2. k += 1
  3. ++k
  4. k++

Несмотря на эту награду (или, возможно, из-за этого), некоторые программисты запутываются и выкашивают такие искажения, как

k = k++;

Если вы не можете понять, что это должно делать, не волнуйтесь: никто не может. Это выражение содержит две разные попытки изменить kзначение ( k =часть и k++часть), и поскольку в C нет правила, чтобы сказать, какая из попыток модификации "выиграет", такое выражение формально не определено , что означает не только то, что у него нет определенного значения, но вся программа, содержащая его, является подозрительной.

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

Смотрите также эти канонические SO ответы на все вопросы, касающиеся неопределенного поведения такого рода.

Но вы не спросили о k=k++роли. Вы спрашивали о первой запутанной части, +(+k--)!=0состоянии. Это выглядит странно, потому что это странно. Никто никогда не будет писать такой код в реальной программе. Так что нет смысла учиться понимать это. (Да, это правда, изучение границ системы может помочь вам узнать о ее тонкостях, но в моей книге есть довольно четкая грань между творческими, заставляющими думать исследованиями по сравнению с глупыми, оскорбительными исследованиями, и это выражение очень четко неправильная сторона этой линии.)

Во всяком случае, давайте рассмотрим +(+k--)!=0. (И после этого давайте забудем все об этом.) Любое подобное выражение следует понимать изнутри. Я полагаю, вы знаете, что

k--

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

Но тогда что делает +? Это унарный плюс, а не бинарный плюс. Это как унарный минус. Вы знаете, что двоичный минус делает вычитание: выражение

a - b

вычитает б из а. И вы знаете, что унарный минус отрицает вещи: выражение

-a

дает вам минус. То, что +делает унарный , это ... в основном ничего. +aдает вам aзначение, после изменения положительных значений на положительные и отрицательных значений на отрицательные. Итак, выражение

+k--

дает вам все, что k--дал вам, то есть, kстарая ценность.

Но мы еще не закончили, потому что у нас есть

+(+k--)

Это просто берет то, что +k--дало тебе, и +снова относится к нему одинарно . Так что это дает вам все, что +k--дало вам, то, что k--дало вам, что было kстарой ценностью.

Итак, в конце концов, состояние

while(+(+k--)!=0)

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

while(k-- != 0)

Сделал бы. (Это также делает то же самое, что while(+(+(+(+k--)))!=0)могло бы сделать даже более сложное на вид условие . И эти скобки на самом деле не нужны; это также делает то же самое, while(+ + + +k--!=0)что и сделал бы.)

Даже выяснить, что такое «нормальное» состояние

while(k-- != 0)

делает это довольно сложно. В этом цикле происходит две вещи: поскольку цикл может выполняться несколько раз, мы собираемся:

  1. продолжать делать k--, чтобы сделать все kменьше и меньше, но и
  2. продолжайте делать тело цикла, что бы это ни делало.

Но мы выполняем свою k--роль сразу же, прежде чем (или в процессе) решить, предпринять ли еще одно путешествие по циклу. И помните, что k--«возвращает» старое значение k, прежде чем уменьшать его. В этой программе начальное значение kравно 0. Так k--что "вернуть" старое значение 0, а затем обновить kдо -1. Но остальная часть условия != 0- но, как мы только что видели, в первый раз, когда мы проверили условие, мы получили 0. Таким образом, мы не будем совершать никаких циклов, поэтому не будем пытаться выполнить проблемное утверждение k=k++на всех.

Другими словами, в этом конкретном цикле, хотя я сказал, что «происходит что-то вроде двух вещей», оказывается, что вещь 1 происходит один раз, а вещь 2 - ноль раз.

В любом случае, я надеюсь, что теперь достаточно понятно, почему это плохое оправдание для программы приводит к выводу -1 в качестве окончательного значения k. Обычно я не люблю отвечать на вопросы викторины, подобные этим - это похоже на мошенничество - но в этом случае, поскольку я так громогласно не согласен со всем смыслом упражнения, я не возражаю.

Стив Саммит
источник
1
Придуманные примеры типичны для любого базового класса. Как еще написать краткий вопрос о приоритетах операторов на экзамене? Я преподаю математику, и если бы у меня был никель за каждый раз, когда студент спросил: «Когда я собираюсь сделать это в реальной жизни?» ...
Скотт
4
@ Скотт Там надумано, а потом надумано . Предположим, вы инструктор по вождению. Предположим, вы попросили своего ученика проехать через центр города, бьющего его по голове бейсбольной битой. Возможно, студент изучит потенциально полезный навык. Но я называю это оскорблением. И я придерживаюсь своего мнения, что код в этом вопросе оскорбительный, с отрицательной педагогической ценностью.
Стив Саммит
1
«Я придерживаюсь своего мнения», это справедливо, но ключевое слово здесь - «мнение». Ответ StackOverflow, возможно, не является оптимальным местом для выражения своего мнения. :)
Скотт
1
Для протокола, я довольно значительно отредактировал этот ответ, так как он был принят и проголосовал. Это все еще говорит в основном то же самое, хотя.
Стив Саммит
11

На первый взгляд кажется, что этот код вызывает неопределенное поведение, однако это не так.

Сначала давайте правильно отформатируем код:

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
        k=k++;
    printf("%d\n", k);  
    return 0;
}

Итак, теперь мы можем видеть, что утверждение k=k++;находится внутри цикла.

Теперь давайте проследим программу:

Когда условие цикла сначала оценивается, kимеет значение 0. Выражение k--имеет текущее значение k, равное 0, и kуменьшается как побочный эффект. Таким образом, после этого утверждения значение k-1.

Начало +в этом выражении не влияет на значение, поэтому +k--оценивается в 0 и аналогично +(+k--)оценивается в 0.

Затем !=оператор оценивается. Так 0!=0как false, тело цикла не вводится . Если бы тело было введено, вы бы вызвали неопределенное поведение, потому что k=k++и чтение, и запись kбез точки последовательности. Но петля не введена, поэтому нет UB.

Наконец, kпечатается значение -1.

dbush
источник
1
Это открытый, экзистенциальный, философский, невесомый вопрос, не является ли неопределенное поведение, которое не выполнено, неопределенным. Разве это не неопределенно? Я так не думаю.
Стив Саммит
2
@SteveSummit В этом нет абсолютно ничего существенного, философского, неуместного. if (x != NULL) *x = 42;Это не определено когда x == NULL? Конечно, нет. Неопределенное поведение не происходит в частях кода, которые не выполняются. Слово « поведение» - это подсказка. Код, который не выполняется, не имеет поведения, неопределенного или иного.
нет. местоимения м.
1
@SteveSummit Если неопределенное поведение, которое не выполнено, лишит законной силы всю программу, ни одна программа на C не имела бы определенного поведения. Потому что программы на Си всегда являются просто циклом / условием, если они не выполняются в UB. Возьмем для примера простую итерацию массива: она повторяется до тех пор, пока проверка не завершится неудачно. Выполнение следующей итерации цикла будет неопределенным поведением, но, поскольку оно никогда не выполняется, все в порядке.
cmaster - восстановить
3
Я не собираюсь спорить об этом, потому что я не занимаюсь языковым адвокатом. Но держу пари, что если вы зададите этот вопрос и пометите его языковым юристом, вы можете начать оживленную дискуссию. Обратите внимание, что k=k++качественно отличается от *x=42. Последний четко определен, если xявляется допустимым указателем, но первый не определен, несмотря ни на что. (Я признаю, что вы, возможно, правы, но, опять же, я не собираюсь спорить об этом, и я все больше обеспокоен тем, что нас мастерски контролировали.)
Steve Summit
1
«Последний четко определен, если x является допустимым указателем, но первый не определен, несмотря ни на что». Только целые программы могут иметь неопределенное поведение. Это понятие не применимо к фрагментам кода, взятым отдельно.
нет. местоимения м.
3

Вот версия этого, которая показывает приоритет оператора:

+(+(k--))

Два унарных +оператора ничего не делают, поэтому это выражение в точности эквивалентно k--. Человек, который написал это, скорее всего, пытался возиться с вашим разумом.

СС Энн
источник