Неопределенное поведение в данном коде?

Каково возвращаемое значение f(p,p), если значение p инициализируется до 5 перед вызовом? Обратите внимание, что первый параметр передается по ссылке, тогда как второй параметр передается по значению.

int f (int &x, int c) {
       c = c - 1;
       if (c==0) return 1;
       x = x + 1;
       return f(x,c) * x;
}

Варианты:

  1. 3024
  2. 6561
  3. 55440
  4. 161051

Я пытаюсь объяснить:


В этом коде будет четыре рекурсивных вызова с параметрами (6,4), (7,3), (8,2) и (9,1). Последний вызов возвращает 1. Но из-за передачи по ссылке, x во всех предыдущих функциях теперь равен 9. Следовательно, значение, возвращаемое f(p,p), будет 9 * 9 * 9 * 9 * 1 = 6561.


Этот вопрос из конкурсного экзамена GATE, ( см. Q.no.-42). Ключ ответа задается GATE "Marks to all" (означает, что нет правильной опции). Ключ set-C, Q.no.-42. Где-то объясняется как:

В GATE 2013 всем были даны метки, так как один и тот же код на C/C++ вызывает неопределенное поведение. Это потому что * не является точкой последовательности в C/C++. Правильный код должен заменить

return f(x,c) * x;

с

 res = f(x,c);
 return res * x;

Но данный код работает нормально. Ключ GATE неверен? Или это действительно ошибка с вопросом?

2 ответа

Решение
   return f(x,c) * x;

Результат этой операции зависит от порядка, в котором оцениваются две вещи. Поскольку вы не можете предсказать порядок их оценки, вы не можете предсказать результат этой операции.

C++03 глава 5:

Если не указано иное, порядок вычисления операндов отдельных операторов и подвыражений отдельных выражений и порядок возникновения побочных эффектов не определены.

Так что в случае f(x,c) * xпорядок вычисления операндов не определен, а это означает, что вы не можете знать, будет ли вычисляться первым левый или правый операнд.

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

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

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

Другие вопросы по тегам