Бесконечный цикл против бесконечной рекурсии.Оба не определены? - PullRequest
7 голосов
/ 17 апреля 2019

Бесконечные циклы без побочного эффекта - неопределенное поведение. См. здесь для примера из cppreference . Пример гораздо проще:

int foo() {
    while(true) {}
    return 42;
}

Теперь рассмотрим почти эквивалентный

int bar() {
    if (true) return bar();
    return 42;
}

Вызывает ли это также неопределенное поведение?

Или по-другому: Какой класс ошибки является бесконечной рекурсией в зависимости от языка?

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

Ответы [ 2 ]

10 голосов
/ 17 апреля 2019

Нет, нет никакой разницы. [basic.progress] p1

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

  • кончить,

  • вызов функции библиотечного ввода-вывода,

  • выполнить доступ через энергозависимое значение или

  • выполнить операцию синхронизации или атомарную операцию.

Неважно, как у вас есть бесконечный цикл; если это не делает ни одного из пунктов выше, вы получаете UB. Включая следующее:

int bar(int cond) {
    if (cond == 42) bar(cond);
    return 42;
}
bar(some_user_input);

Компилятору разрешено предполагать, что some_user_input никогда не будет 42.

0 голосов
/ 23 апреля 2019

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

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

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

if (x > 1000) foo(x);
while ( x <= 1000)
  somethingWithNoSideEffectsThatCantAffectX();

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

volatile int yy;
void test(int x, int *restrict arr)
{
  int y;
  do
    x=arr[x];
  while(x & 1);
  y=yy;
  if (y)
    printf("%d\n",x);
}

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

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

...