Может ли код, который никогда не будет выполнен, вызывает поведение undefined?

Код, вызывающий поведение undefined (в этом примере, деление на ноль) никогда не будет выполнен, является ли поведение программы undefined <? >

int main(void)
{
    int i;
    if(0)
    {
        i = 1/0;
    }
    return 0;
}

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

Итак, любые идеи?

Ответ 1

Посмотрите, как стандарт C определяет термины "поведение" и "поведение undefined".

Ссылки на проект N1570 стандарта ISO C 2011; Мне неизвестны какие-либо существенные различия в любом из трех опубликованных стандартов ISO C (1990, 1999 и 2011 гг.).

Раздел 3.4:

поведение
внешний вид или действие

Хорошо, это немного расплывчато, но я бы утверждал, что данное утверждение не имеет "внешнего вида" и, конечно, никакого "действия", если только оно не выполняется.

Раздел 3.4.3:

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

В нем говорится "при использовании" такой конструкции. Слово "использование" не определяется стандартом, поэтому мы возвращаемся к общему английскому значению. Конструкция не используется, если она никогда не выполнялась.

В этом определении есть примечание:

ПРИМЕЧАНИЕ. Возможное поведение undefined варьируется от игнорирования ситуации полностью с непредсказуемыми результатами, вести себя во время перевода или выполнения программы документированным образом, характерным для (с выдачей диагностического сообщения или без него), чтобы прекращение перевода или исполнения (с выдачей диагностическое сообщение).

Поэтому компилятору разрешено отклонять вашу программу во время компиляции, если ее поведение undefined. Но моя интерпретация этого заключается в том, что он может сделать это только в том случае, если он может доказать, что каждое выполнение программы встретит поведение undefined. Это подразумевает, я думаю, что это:

if (rand() % 2 == 0) {
    i = i / 0;
}

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

В практическом плане программы должны иметь возможность выполнять тесты времени выполнения для защиты от вызова поведения undefined, и стандарт должен позволить им это сделать.

Ваш пример:

if (0) {
    i = 1/0;
}

который никогда не выполняет деление на 0. Очень распространенная идиома:

int x, y;
/* set values for x and y */
if (y != 0) {
    x = x / y;
}

Разделение, конечно, имеет поведение undefined, если y == 0, но оно никогда не выполняется, если y == 0. Поведение четко определено и по той же причине, что ваш пример хорошо определен: поскольку потенциальное поведение undefined никогда не может произойти.

(Если INT_MIN < -INT_MAX && x == INT_MIN && y == -1 (да, целочисленное деление может переполняться), но это отдельная проблема.)

В комментарии (после удаления) кто-то указал, что компилятор может оценивать постоянные выражения во время компиляции. Это верно, но не актуально в этом случае, поскольку в контексте

i = 1/0;

1/0 не является постоянным выражением.

Константное выражение - это синтаксическая категория, которая сводится к условному выражению (которое исключает присвоения и выражения для запятой). Выражение константы производительности появляется в грамматике только в контекстах, которые на самом деле требуют постоянного выражения, например, метки ярлыков. Поэтому, если вы пишете:

switch (...) {
    case 1/0:
    ...
}

то 1/0 является постоянным выражением - и которое нарушает ограничение в 6.6p4: "Каждое константное выражение должно оцениваться константой, находящейся в диапазоне представимых значения для его типа. ", поэтому требуется диагностика. Но в правой части присваивания не требуется константное выражение, а только условное выражение, поэтому ограничения на константные выражения не применяются. Компилятор может оценить любое выражение, которое оно умеет во время компиляции, но только если поведение такое же, как если бы оно было оценено во время выполнения (или, в контексте if (0), не оценивалось во время выполнения().

(То, что выглядит точно как константное выражение, не обязательно является константным выражением, так же как и в x + y * z последовательность x + y не является аддитивным выражением из-за контекста, в котором она появляется.)

Это означает сноску в разделе 6.6 N1570, которую я собираюсь привести:

Таким образом, в следующей инициализации,
    static int i = 2 || 1 / 0;
выражение является допустимым целочисленным постоянным выражением со значением 1.

на самом деле не имеет отношения к этому вопросу.

Наконец, есть несколько вещей, которые определены, чтобы вызвать поведение undefined, которое не связано с тем, что происходит во время выполнения. Приложение J, раздел 2 стандарта C (снова см. Проект N1570) перечисляет вещи, которые вызывают поведение undefined, собранное из остальной части стандарта. Некоторые примеры (я не утверждаю, что это исчерпывающий список):

  • Непустой исходный файл не заканчивается символом новой строки, которому сразу не предшествует символ обратной косой черты или заканчивается частичным токен предварительной обработки или комментарий
  • Конкатенация токена создает последовательность символов, соответствующую синтаксису универсального символьного имени
  • Символ, не содержащий базовый набор символов источника, встречается в исходном файле, за исключением идентификатора, символьной константы, строки литерал, имя заголовка, комментарий или токен предварительной обработки, который является никогда не преобразовывается в токен
  • Идентификатор, комментарий, строковый литерал, символьная константа или имя заголовка содержит недопустимый многобайтовый символ или не начинается и заканчивается в начальном состоянии сдвига
  • Тот же идентификатор имеет как внутреннюю, так и внешнюю связь в одной и той же единице перевода

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

Ответ 2

Эта статья обсуждает этот вопрос в разделе 2.6:

int main(void){
      guard();
      5 / 0;
}

Авторы считают, что программа определена, когда guard() не заканчивается. Они также обнаруживают отличительные понятия "статически undefined" и "динамически undefined", например:

Цель стандартного 11 заключается в том, что в общем случае ситуации статически статичны undefined, если их легко создать. Только когда код может быть сгенерирован, тогда ситуация может быть undefined динамически.

11) Частная переписка с членом комитета.

Я бы рекомендовал посмотреть всю статью. Взятый вместе, он рисует последовательную картину.

Тот факт, что авторы статьи обсуждали вопрос с членом комитета, подтверждает, что стандарт в настоящее время нечеткий в ответе на ваш вопрос.

Ответ 3

В этом случае поведение undefined является результатом выполнения кода. Поэтому, если код не выполняется, не существует поведения undefined.

Не выполненный код может вызывать поведение undefined, если поведение undefined было результатом только объявления кода (например, если какой-либо случай переменной тени был undefined).

Ответ 4

Я бы воспользовался последним абзацем этого ответа: fooobar.com/questions/71866/...

... UB - это проблема времени выполнения, а не проблема с компоновкой...

Итак, нет, нет вызываемого UB.

Ответ 5

В стандарте говорится, что, насколько я помню, он разрешал делать что-либо с этого момента, правило нарушилось. Может быть, есть какие-то особые события с каким-то глобальным вкусом (но я никогда не слышал и не читал о чем-то подобном)... Поэтому я бы сказал: "Нет, это не может быть UB, потому что, если поведение хорошо определено 0, всегда false, поэтому правило не может быть нарушено во время выполнения.

Ответ 6

Только когда стандарт вносит изменения, и ваш код внезапно перестает "никогда не запускаться". Но я не вижу никакого логического способа, которым это может вызвать поведение undefined. Это не вызывает ничего.

Ответ 7

В отношении поведения undefined часто трудно отделить формальные аспекты от практических. Это определение поведения undefined в стандарте 1989 года (у меня нет более новой версии, но я не ожидаю, что это существенно изменится):

1 undefined behavior
  behavior, upon use of a nonportable or erroneous program construct or of
  erroneous data, for which this International Standard imposes no requirements
2 NOTE Possible undefined behavior ranges from ignoring the situation completely
  with unpredictable results, to behaving during translation or program execution
  in a documented manner characteristic of the environment (with or without the
  issuance of a diagnostic message), to terminating a translation or
  execution (with the issuance of a diagnostic message).

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

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

Ответ 8

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

Я думаю, что программа не вызывает поведение undefined.

Отчет о дефектах № 109 описывает аналогичный вопрос и говорит:

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

Ответ 9

Это зависит от того, как определено выражение "поведение w90 > ", и является ли поведение "undefined" для оператора такой же, как "undefined поведение" для программы.

Эта программа выглядит как C, поэтому более глубокий анализ того, что стандарт C, используемый компилятором (как это делали некоторые ответы), является подходящим.

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

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