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

Это поведение undefined C?

Наш класс задавался этим вопросом программированием C:

Вам предоставляется код:

int x=1;
printf("%d",++x,x+1);

Какой вывод будет всегда производить?

Большинство студентов сказали undefined поведение. Может ли кто-нибудь помочь мне понять, почему это так?

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

4b9b3361

Ответ 1

В каждом разумном случае выход, вероятно, будет равным 2. В действительности, у вас есть поведение undefined.

В частности, в стандарте говорится:

Между предыдущей и следующей точками последовательности объект должен иметь измененное значение хранимого значения не более одного раза путем оценки выражения. Кроме того, предыдущее значение должно быть считано только для определения сохраняемого значения.

Перед оценкой аргументов функции есть точка последовательности, а точка последовательности после всех аргументов была оценена (но функция еще не была вызвана). Между этими двумя (т.е. Пока оцениваются аргументы) нет точки последовательности (если аргумент не является выражением, он включает внутри себя, например, с помощью оператора && || или ,).

Это означает, что вызов printf считывает предыдущее значение как для определения сохраняемого значения (т.е. ++x), так и для определения значения второго аргумента (т.е. x+1). Это явно нарушает указанное выше требование, приводящее к поведению undefined.

Тот факт, что вы предоставили дополнительный аргумент, для которого не указан спецификатор преобразования, не приводит к поведению undefined. Если вы указали меньшее количество аргументов, которые задают преобразование, или если (продвинутый) тип аргумента не согласуется с типом спецификатора преобразования, вы получаете поведение undefined, но передача дополнительного параметра не выполняется.

Ответ 2

В любое время, когда поведение программы undefined, все может случиться - классическая фраза заключается в том, что "демоны могут летать из вашего носа" - хотя большинство реализаций не заходят так далеко.

Аргументы функции концептуально оцениваются параллельно (технический термин состоит в том, что между их оценкой не существует точки последовательности). Это означает, что выражения ++x и x+1 могут быть оценены в этом порядке, в обратном порядке или в некотором чередовании. Когда вы изменяете переменную и пытаетесь получить доступ к ее значению параллельно, поведение undefined.

Во многих реализациях аргументы оцениваются последовательно (хотя не всегда слева направо). Таким образом, вы вряд ли увидите что-либо, кроме 2 в реальном мире.

Однако компилятор может генерировать код следующим образом:

  • Загрузите x в регистр r1.
  • Вычислить x+1, добавив 1 к r1.
  • Рассчитайте ++x, добавив 1 к r1. Это нормально, потому что x загружен в r1. Учитывая, как был разработан компилятор, шаг 2 не может изменить r1, потому что это может произойти только в том случае, если x был прочитан, а также записан между двумя точками последовательности. Это запрещено стандартом C.
  • Хранить r1 в x.

И на этом (гипотетическом, но правильном) компиляторе программа будет печатать 3.

(EDIT: передача дополнительного аргумента в printf верна (§7.19.6.1-2 в N1256, благодаря Prasoon Saurav), чтобы указать на это. Также: добавлен пример.)

Ответ 3

Правильный ответ: код создает поведение undefined.

Причиной поведения undefined является то, что два выражения ++x и x + 1 изменяют x и читают x для несвязанной (для модификации) причины, и эти два действия не разделяются точка последовательности. Это приводит к поведению undefined в C (и С++). Требование дано в 6.5/2 стандарта языка C.

Обратите внимание, что поведение undefined в этом случае не имеет абсолютно никакого отношения к тому, что функции printf предоставляется только один спецификатор формата и два фактических аргумента. Чтобы дать больше аргументов printf, чем спецификаторы форматирования в строке формата являются совершенно законными в C. Опять же, проблема связана с нарушением требований оценки выражения языка C.

Также обратите внимание, что некоторые участники этого обсуждения не понимают концепцию поведения undefined и настаивают на его смешивании с понятием неуказанного поведения. Чтобы лучше проиллюстрировать разницу, рассмотрим следующий простой пример

int inc_x(int *x) { return ++*x; }
int x_plus_1(int x) { return x + 1; }

int x = 1;
printf("%d", inc_x(&x), x_plus_1(x));

Вышеприведенный код является "эквивалентным" исходному, за исключением того, что операции, которые включают наш x, завернуты в функции. Что произойдет в этом последнем примере?

В этом коде нет поведения undefined. Но поскольку порядок оценки аргументов printf не указан, этот код вызывает неуказанное поведение, т.е. Возможно, что printf будет называться как printf("%d", 2, 2) или как printf("%d", 2, 3). В обоих случаях вывод действительно будет 2. Однако важным отличием этого варианта является то, что все обращения к x завернуты в точки последовательности, присутствующие в начале и в конце каждой функции, поэтому этот вариант не создает поведение undefined.

Это именно то рассуждение, которое некоторые другие плакаты пытаются применить к исходному примеру. Но это невозможно. В исходном примере создается поведение undefined, которое является совершенно другим зверем. Очевидно, они пытаются утверждать, что на практике поведение undefined всегда эквивалентно неуказанному поведению. Это совершенно фиктивное утверждение, которое указывает только на отсутствие опыта у тех, кто это делает. Исходный код создает поведение undefined, период.

Чтобы продолжить с примера, позвольте изменить предыдущий пример кода на

printf("%d %d", inc_x(&x), x_plus_1(x));

вывод кода станет в целом непредсказуемым. Он может печатать 2 2 или печатать 2 3. Однако обратите внимание, что, хотя поведение непредсказуемо, оно по-прежнему не вызывает поведения undefined. Поведение неуказано, бит не undefined. Неопределенное поведение ограничено двумя возможностями: либо 2 2, либо 2 3. Поведение undefined не ограничивается ничем. Он может форматировать жесткий диск вместо печати. Почувствуйте разницу.

Ответ 4

Большинство студентов сказали undefined поведение. Может ли кто-нибудь помочь мне понять, почему это так?

Потому что порядок, в котором вычисляются параметры функции, не указан.

Ответ 5

Какой вывод будет всегда производить?

Он будет производить 2 во всех средах, о которых я могу думать. Однако строгая интерпретация стандарта C99 делает поведение undefined, поскольку обращения к x не соответствуют требованиям, существующим между точками последовательности.

Большинство студентов сказали undefined поведение. Может ли кто-нибудь помочь мне понять, почему это так?

Теперь я рассмотрю второй вопрос, который я понимаю как "Почему большинство учеников моего класса говорят, что показанный код представляет собой поведение undefined?". и я думаю, что ни один другой плакат не ответил до сих пор. Одна часть учеников вспомнит примеры значений undefined выражений типа

f(++i,i)

Код, который вы даете, соответствует этому шаблону, но студенты ошибочно полагают, что поведение определено так или иначе, потому что printf игнорирует последний параметр. Этот нюанс смущает многих учеников. Другая часть студента будет также хорошо разбираться в стандарте, как Дэвид Торнли и сказать "undefined поведение" по правильным причинам, описанным выше.

Ответ 6

Точки, выполненные в отношении поведения undefined, верны, но есть еще одна морщина: printf может выйти из строя. Это делает файл IO; существует множество причин, по которым он может потерпеть неудачу, и невозможно исключить их, не зная полной программы и контекста, в которой она будет выполнена.

Ответ 7

Повторяя codaddict, ответ равен 2.

printf будет вызываться с аргументом 2, и он напечатает его.

Если этот код помещается в такой контекст, как:

void do_something()
{
    int x=1;
    printf("%d",++x,x+1);
}

Тогда поведение этой функции полностью и недвусмысленно определено. Я, конечно, не утверждаю, что это хорошо или правильно или что значение x определяется впоследствии.

Ответ 8

Вывод будет всегда (для 99,98% наиболее важных компиляторов и систем, совместимых со стандартами) 2.

В соответствии со стандартом это, по-видимому, по определению, "undefined поведение", определение/ответ, который является самооправданным и ничего не говорит о том, что на самом деле может произойти, и особенно почему.

Утилита splint (которая не является средством проверки соответствия std), поэтому программисты с шинами считают это "неуказанным поведением". Это означает, в основном, что оценка (x+1) может дать 1 + 1 или 2 + 1, в зависимости от того, когда действительно выполняется обновление x. Поскольку, однако, выражение отбрасывается (формат printf читает 1 аргумент), выход не изменяется, и мы все же можем сказать, что оно равно 2.

undefined.c: 7: 20: Аргумент 2 изменяет x, используемый аргументом 3 (порядок    оценка фактических параметров undefined): printf ( "% d\n", ++ x, x + 1)  Код имеет неуказанное поведение. Порядок оценки параметров функции или  подвыражения не определены, поэтому, если значение используется и изменяется в  различные места, не разделенные оценкой последовательности, ограничивающей оценку  порядок, то результат выражения не задан.

Как было сказано ранее, неуказанное поведение влияет только на оценку (x+1), а не на весь оператор или другие выражения. Поэтому в случае "неуказанного поведения" мы можем сказать, что результат равен 2, и никто не может возражать.

Но это не неуказанное поведение, похоже, "поведение undefined". И "поведение undefined", похоже, должно быть чем-то, что влияет на весь оператор, а не на одно выражение. Это происходит из-за mistery, где происходит "поведение undefined" (то есть, что именно влияет).

Если бы были мотивации прикрепить поведение "undefined" только к выражению (x+1), как в случае с "неуказанным поведением", мы все равно можем сказать, что вывод всегда (100%) 2. Прикрепление "поведения undefined" только до (x+1) означает, что мы не можем сказать, если он равен 1 + 1 или 2 + 1; это просто "все". Но опять же, что "что-либо" удаляется из-за printf, а это означает, что ответ будет "всегда (100%) 2".

Вместо этого из-за мистических асимметрий поведение < undefined не может быть привязано только к x+1, но на самом деле оно должно влиять, по крайней мере, на ++x (которое, кстати, отвечает за undefined), если не весь оператор. Если он заражает только выражение ++x, выход представляет собой значение "undefined", то есть любое целое число, например. -5847834 или 9032. Если он заражает весь оператор, то вы можете увидеть gargabe на вашем выходе в консоль, вероятно, вам придется остановить программу с помощью ctrl-c, возможно, прежде чем она начнет подавлять ваш процессор.

Согласно городской легенде, "поведение undefined" заражает не только всю программу, но и ваш компьютер и законы физики, так что создатели вашей магии могут быть созданы вашей программой и улетать или съесть вас.

Никакие ответы не объясняют что-либо грамотно по теме. Они просто "о, видите, стандарт говорит это" (и это просто интерпретация, как обычно!). По крайней мере, вы узнали, что существуют "стандарты", и они задают образовательные вопросы (поскольку, конечно, не забывайте, что ваш код неправильный, независимо от undefined/неуказанного поведения и других стандартные факты), неиспользуемые логические аргументы и бесцельные глубокие исследования и понимание.