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

Является результатом printf ( "% d% d", С++, c); также undefined?

Недавно я наткнулся на сообщение Каков правильный ответ для cout < С++ < c;? и задавался вопросом, выводится ли вывод

int c = 0;  
printf ("%d %d", c++, c);  

также undefined?

Я читал лекции о том, что пост-исправить и префиксные операторы увеличивают значение только после получения точки с запятой. Итак, по мне, выход 0 0 правильный!

4b9b3361

Ответ 1

Я читал на лекциях, что постфиксные и префиксные операторы увеличивают значение только после получения точки с запятой.

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

Точно, когда применяется побочный эффект пре- или постфикса ++ и --, не указывается, кроме требования, которое оно выполняется до следующей точки последовательности. В выражении типа

x = a++ * b

a может быть обновлен сразу после оценки a++ или обновление может быть отложено до тех пор, пока a++ * b не будет оценен, а результат будет назначен x, или где-либо между ними.

Вот почему выражения типа i++ * i++ и printf("%d %d", c++, c) и a[i++] = i и множество других - все плохое. Вы получите разные результаты на основе компилятора, параметров оптимизации, окружающего кода и т.д. Стандарт языка явно оставляет поведение undefined таким образом, что компилятор не обязан "делать правильную вещь", какова бы ни была правильная вещь, Помните, что определение поведения undefined -

3.4.3

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

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

3 ПРИМЕР. Примером неустановленного поведения является поведение на целочисленном потоке.

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

Обратите внимание, что компилятор может попытаться обнаружить эти случаи и выдать диагностику; printf("%d %d", c++, c); было бы достаточно легко поймать, но это было бы ошибкой обнаружить в общем случае. Представьте, что это было написано printf("%d %d", (*p)++, c); если p указывает на c, то поведение undefined, в противном случае это нормально. Если p назначается в другой единицы перевода, тогда нет способа узнать во время компиляции, является ли это проблемой или нет.

Эту концепцию нетрудно понять, но она является одним из наиболее последовательно непонятых (и неправильно обученных) аспектов языка С. Несомненно, именно поэтому спецификации языка Java и С# задают специфический порядок оценки для всего (все операнды оцениваются слева направо, и все побочные эффекты применяются немедленно).

Ответ 2

Я читал лекции о том, что значения post-fix и prefix операторов увеличиваются только после получения точки с запятой

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

Порядок оценки аргументов функции не указан. Нет никакой гарантии, что аргументы функции будут оцениваться в порядке (1, 2, N), поэтому нет гарантии, что приращение будет оценено до передачи второго аргумента.

Итак, по мне, выход 0 0 правильный!

Нет, поведение undefined, поэтому вы не можете обоснованно утверждать, что вывод будет 0 0.

Ответ 3

Поведение программы undefined, поскольку оно нарушает требования 6.5 выражений:

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

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

Ответ 4

Поведение будет определенно undefined из-за undefined порядка оценки параметров. Вы можете доказать это "undefined output", выполняя случайное тестирование:

printf("%d %d\n", c++, c);
// result: 0 1
printf("%d %d %d\n", c++, c, c++);
// result: 1 2 0
printf("%d %d %d %d\n", c++, c++, c++, c);
// result: 2 1 0 3
printf("%d %d %d %d\n", c++, c, c++, c);
// result: 1 2 0 2
printf("%d %d %d %d\n", c++, c, c, c);
// result: 0 1 1 1

Ответ 5

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

Технически неверно, что приращение происходит только после точки с запятой, кстати. Стандартные гарантии заключаются в том, что приращение произойдет не позднее точки с запятой. [На самом деле, в вашем случае, я считаю, что стандарт гарантирует, что он будет происходить до того, как управление будет передано функции printf(), но теперь этот ответ начинает вращаться в сферы педантичных мелочей, поэтому позвольте мне ответить на этот вопрос отдохните там!]

В любом случае, короче, вы правы. Поведение undefined.

Обновление: как правило @R.. справедливо замечает, поведение undefined происходит из-за отсутствия точки последовательности между аргументами. Стандарт довольно осторожен относительно неопределенных причастий и undefined, поэтому исправление принято с благодарностью.

Ответ 6

Эта программа демонстрирует сочетание неуказанного поведения и undefined поведение. Начиная с неуказанного поведения, черновик стандарта C99 в разделе 6.5 paragraph 3 говорит:

Группировка операторов и операндов обозначается синтаксисом .74) За исключением случаев, указанных ниже (для функции-call(), & &, ||,?: И запятые), порядок оценки подвыражений и порядок, в котором происходят побочные эффекты, не определены.

Он также говорит, что, как указано ниже, и конкретно цитирует function-call (), поэтому мы видим, что позже в проекте стандарта в разделе 6.5.2.2 Функция вызывает абзац 10:

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

Таким образом, мы не знаем, будет ли чтение C или оценка С++ первым в этой строке кода:

printf ("%d %d", c++, c); 

более того, в разделе 6.5.2.4 Операторы приращения и декремента Postfix 2 говорят:

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

Итак, все, что мы знаем, заключается в том, что при выполнении приращения post c будет обновляться после того, как его значение будет прочитано, но до следующей точки последовательности который находится прямо перед тем, как printf вызывается, но ничего другого. Что касается поведения undefined, если мы рассмотрим раздел 6.5 абзац 2 из черновика, говорится:

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

В выражении printf c используется предварительное значение для оценки как С++, так и C, и поэтому мы теперь находимся на территории undefined.