Что бы ни было сделано программой до того, как она вызовет неопределенное поведение, конечно, уже сделано.
Таким образом, printf()
послал бы "0 \ n" в поток stdout
. То, действительно ли эти данные поступили на устройство, зависит от того, является ли этот поток небуферизованным, буферизованным или буферизованным строкой.
Опять же, я полагаю, что возможно, что неопределенное поведение, выполненное после завершенных, четко определенных действий, может нанести ущерб в той степени, в которой кажется, что четко определенное поведение не завершилось правильно. Я думаю, что-то вроде одного из тех вещей, «если дерево падает в лесу…».
Обновление, чтобы учесть, что будущее неопределенное поведение означает, что все ставки отключены даже до того, как программа начнет выполняться ...
Вот что говорит стандарт C99 об изменении значения объекта более одного раза между точками последовательности:
Между предыдущей и следующей точкой последовательности объект должен иметь свое сохраненное значение
модифицируется не более одного раза путем вычисления выражения.
И в стандарте также говорится о доступе к объекту:
доступ
<execution-time action> to read or modify the value of an object
NOTE 1 Where only one of these two actions is meant, ``read'' or ``modify'' is used.
NOTE 2 "Modify'' includes the case where the new value being stored is the same as the previous value.
NOTE 3 Expressions that are not evaluated do not access objects.
Я не думаю, что изменение объекта более одного раза между точками последовательности является «неопределенным поведением» во время перевода, поскольку объекты не доступны / не изменены во время перевода.
Несмотря на это, я согласен, что компилятор, который диагностирует это неопределенное поведение во время компиляции, был бы полезен, но я также думаю, что этот вопрос более интересен, если его применять только к программам, которые были успешно скомпилированы. Итак, давайте немного изменим вопрос, чтобы создать ситуацию, когда компилятор не может диагностировать неопределенное поведение во время перевода:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int c[] = { 0, 1, 2, 3 };
int *p1 = &c[0];
int *p2 = &c[1];
if (argc > 1) {
p1 = &c[atoi(argv[1])];
}
if (argc > 2) {
p2 = &c[atoi(argv[2])];
}
printf("before: %d, %d\n", *p1, *p2);
printf("after: %d, %d\n", ++(*p1),++(*p2)); /* possible undefined behavior */
return 0;
}
В этой программе неизвестное поведение даже не может существовать во время трансляции - оно происходит только в том случае, если входные данные программы указывают на то, что должен обрабатываться тот же элемент массива (или может возникнуть другой тип неопределенного поведения, если вход указывает недопустимые значения индекса).
Итак, давайте зададим тот же вопрос с этой программой: что стандарт говорит о том, что может произойти с первыми printf()
результатами или побочными эффектами?
Если входные данные предоставляют допустимые значения индекса, неопределенное поведение может произойти только после первого printf()
. Предположим, что входные данные argv[1] == "1"
и argv[2] == "1"
: реализация компилятора не имеет свободы определять перед первым printf()
, что, поскольку в определенный момент в программе произойдет неопределенное поведение, разрешено пропускать первые printf()
и перейдите прямо к его неопределенному поведению форматирования жесткого диска (или любых других ужасов, которые могут произойти).
Учитывая, что компилятор согласен с соглашением перевести программу, обещание будущего неопределенного поведения не дает компилятору свободы делать все, что он хочет, до того, как это неопределенное поведение действительно будет иметь место. Конечно, как я упоминал ранее, ущерб, нанесенный неопределенным поведением, может разрушить предыдущие результаты, но эти результаты должны были произойти.