Является ли следующее утверждение действительным в ANSI C? Он вообще действителен?

Во время подготовки к экзамену по ANSI C я столкнулся со следующим вопросом -

Действительно ли следующее утверждение?
Если нет, внесите необходимые изменения, чтобы сделать его действительным.

Исходное утверждение: test(i++,i++); оно недействительно, поскольку поведение не определено в соответствии с K&R p202.

Порядок оценки аргументов не указан

Но могу ли я изменить его на следующее утверждение? test(i+=2, i+=3)?

Проблема в том, что я не видел такой записи ни в K&R, ни в каком другом источнике. Но XCode скомпилирует его и запустит без предупреждения.


person Anatoly    schedule 08.05.2015    source источник
comment
Связано: stackoverflow.com/questions/367633/   -  person Phillip    schedule 08.05.2015
comment
Связано: stackoverflow.com/q/4176328/694576   -  person alk    schedule 08.05.2015
comment
Принятый ответ неверен. , в аргументах — это точка последовательности; только порядок оценки аргументов *unspecified. Нет неопределенного поведения.   -  person Antti Haapala    schedule 10.05.2015


Ответы (5)


Оба являются допустимыми операторами, т. е. допустимыми C, и в обоих случаях поведение не определено.

person Jack Whitham    schedule 08.05.2015
comment
Это зависит от того, что вы подразумеваете под действительным. Многие люди будут утверждать, что программа с неопределенным поведением недействительна, и эти утверждения, по крайней мере, так же законны, как и ваши. - person ; 08.05.2015
comment
Если вы так говорите, что тогда недействительно? - person dhein; 08.05.2015
comment
@hvd, сэр, я думаю, что говоря valid OP, означало valid для компилятора. - person Sourav Ghosh; 08.05.2015
comment
ааа .... Я понял, на самом деле я ничего не изменил, переписав свое выражение, и мне нужно создать дополнительные переменные, которые будут вычисляться до вызова функции. Спасибо. - person Anatoly; 08.05.2015
comment
Не путайте корректные (синтаксически) и правильные (или строго соответствующие Стандарту) программы. - person Michael Foukarakis; 08.05.2015
comment
Существует множество случаев неопределенного поведения, которые, тем не менее, допустимы, т. е. приняты стандартом C и компилятором C. Программисты на C используют некоторые из них все время — рассмотрим, например, как округляются числа с плавающей запятой. Очевидно, что не рекомендуется писать test(x++,x++), но компилятор C может только выдать предупреждение, если вы это сделаете. - person Jack Whitham; 08.05.2015
comment
@JackWhitham: Это явно неверно. Мало того, что компиляторы могут делать все, что захотят, с неопределенным поведением, они фактически предполагают, что программа не содержит UB, чтобы выполнить оптимизацию. - person Michael Foukarakis; 08.05.2015
comment
@Jack Whitham: Можно утверждать, что компилятор, который будет выдавать ошибки вместо предупреждения об утверждениях, которые, как доказано, содержат недопустимое поведение, просто будет демонстрировать какое-то интересное и полезное неопределенное поведение. - person chqrlie; 08.05.2015
comment
@MichaelFoukarakis Какая часть явно ложна? Программы часто содержат операторы и выражения, поведение которых точно не определено стандартом C. Я даже привел пример этого, а именно обработку чисел с плавающей запятой. Вы действительно утверждаете, что стандарт C точно определяет, какие операции с плавающей запятой должны выполняться? Если нет, то, безусловно, вам придется признать, что зависимость от неопределенного поведения на самом деле довольно распространена. - person Jack Whitham; 08.05.2015
comment
@JackWhitham: Более того, существует множество случаев, когда поведение не определено стандартом C, но реализация обещает определенное поведение. Например, стандарт C не говорит, должно ли -1‹‹2 давать -4 или должно ли это отрицать законы причинности, но многие реализации документально подтверждают, что сдвиг отрицательных чисел влево ведет себя как умножение, и совершенно законно используйте левый сдвиг таким образом в таких реализациях. - person supercat; 08.05.2015
comment
@supercat, вы путаете undefined с unspecified. - person Antti Haapala; 10.05.2015
comment
Поскольку эта программа имеет неопределенное поведение, ее даже не нужно компилировать — то есть существует класс ошибок, называемый неопределенным поведением, некоторые соответствующие компиляторы на некоторых входных данных не компилировать вообще для некоторых из этих классов ошибок. - person Antti Haapala; 10.05.2015
comment
@AnttiHaapala: если x и y являются указателями на разные объекты, x<y приводит к неопределенному поведению; он не просто возвращает неопределенный результат. Было бы почти невозможно написать операционную систему, подобную Unix, на C (вот почему C был изобретен в первую очередь!), если бы нельзя было определить, идентифицирует ли произвольный указатель местоположение внутри объекта. Ожидалось, что системы, которые могут удобно определять операторы таким образом, что needle>=haystack && needle < hastack+size будет подразумевать, что игла находится в стоге сена, будут делать это; те, которые не смогли... - person supercat; 11.05.2015
comment
... не сможет эффективно выполнять определенные виды кода, но, тем не менее, позволит использовать язык программирования C для других задач, не требующих такой возможности. Кстати, мое предположение о том, почему < дает неопределенное, а не неопределенное поведение, заключается в том, чтобы учесть возможность того, что система, в которой получение < согласованных результатов для несвязанных указателей будет дорогостоящим, может предоставить три варианта: (1) такие сравнения генерируют медленный код что дает последовательное ранжирование; (2) такие сравнения приводят к непоследовательному ранжированию; (3) иметь ловушку таких сравнений, по крайней мере, в... - person supercat; 11.05.2015
comment
... тестирование сборок при попытке выяснить, почему, например. код, который должен сортировать указатели, оставляет их несортированными [многие алгоритмы сортировки будут плохо работать, если x>y и y>z не подразумевают x>z]. С практической точки зрения сравнение произвольных указателей с получением неопределенных результатов ненамного полезнее, чем Undefined Behavior, и отлов таких сравнений может быть полезнее, чем получение фиктивных результатов в системах, которые не могут дать хороших результатов. - person supercat; 11.05.2015

Чтобы дополнить существующие ответы, ключевым моментом является утверждение

test(i+=2, i+=3)

вызывает неопределенное поведение столько, сколько

test(i++,i++);

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

person Sourav Ghosh    schedule 08.05.2015

Операторы синтаксически допустимы как до, так и после изменения. Но все равно проблема останется. Если вы изменяете объект в аргументе, а порядок оценки не указан.

C99 Раздел 6.5.2.2 Параграф 10

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

Согласно Разделу 3.4.4 Параграф 1

неопределенное поведение

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

С другой стороны, в разделе 3.4.3, параграф 1 говорится

неопределенное поведение

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

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

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

test (i, i+1);
i += 2;

OR

test (i+1, i);
i+= 2;

В зависимости от того, какой порядок вы хотите.

person phoxis    schedule 08.05.2015
comment
Интересный! Порядок на самом деле не неопределен, а просто не определен. Это другое. Но между оценкой аргументов нет точки последовательности, поэтому поведение по-прежнему не определено. - person chqrlie; 08.05.2015
comment
Что делает его поведение неопределенным, так это множественный побочный эффект одной и той же переменной i между двумя точками последовательности. неуказанное поведение будет следующим: test(printf("arg1\n"), printf("arg2\n")); - person chqrlie; 08.05.2015
comment
@chqrlie Я не могу это понять. Можете ли вы уточнить другой побочный эффект, который делает поведение неопределенным, а не неопределенным? - person phoxis; 08.05.2015
comment
Ответ Сурава Гоша прост и ясен: изменение переменной одной и той же более одного раза без промежуточной точки последовательности вызывает неопределенное поведение. В фрагменте кода OP нет точки последовательности между i++ и i++. В моем примере есть один при вводе printf(), поэтому, даже если printf изменяет одни и те же переменные (это так!), порядок выполнения обоих вызовов не указан, но не неопределен. - person chqrlie; 08.05.2015
comment
Поэтому на самом деле это должно быть следствием обоих. - person phoxis; 08.05.2015
comment
Нет: вы либо получите arg1\narg2\n, либо arg2\narg1\n на stdout до того, что сделает test(). - person chqrlie; 08.05.2015
comment
Не на приведенном вами примере функции, я могу это понять. Что касается исходного вопроса, да, между двумя аргументами нет точек следования, также как указано, поведение не указано. Поэтому поведение в целом не определено. Хотел мнение по этому поводу. - person phoxis; 08.05.2015
comment
неуказанный порядок означает, что компилятор может выбрать один или другой. неопределенное поведение означает все может случиться. - person chqrlie; 08.05.2015
comment
Это правильно, я хочу сказать, в этом случае, если это не определено или не указано. - person phoxis; 08.05.2015
comment
test(i++, i++) вызывает неопределенное поведение, тогда как test(a(), b()) просто имеет неопределенное поведение. - person chqrlie; 08.05.2015

Как уже отмечали другие, поведение в обоих случаях не определено, даже если код синтаксически корректен в обоих случаях. Я предполагаю, что в вопросе «действительный» означает «правильный», как в строго соответствующей программе C. Чтобы сделать утверждение правильным, вы должны сначала узнать/вывести его намерение.

На самом деле это может быть невозможно без какого-либо внешнего источника, сообщающего вам, что это за намерение, но давайте предположим, что программист хочет вызвать функцию test с параметрами (i+1, i+2) (в порядке появления). Было бы лучше всего сообщить об этом намерении просто:

test (i + 1, i + 2);
i += 2;

избежание каких-либо вредных эффектов, вызванных неуказанным порядком оценки аргументов функции.

person Michael Foukarakis    schedule 08.05.2015

Это предложение сбивает с толку: test(i++,i++); оно недопустимо, поскольку поведение не определено в соответствии с K&R p202 .

Правда в том, что это утверждение всегда было недействительным в C. От оригинальной спецификации C Кернигана и Ритчи в их книге Язык программирования C до последнего стандарта C11, опубликованного несколько лет назад, включая более старый стандарт C99 и устаревший стандарт C89, также известный как ANSI C. Только не по указанной причине.

Порядок, в котором оцениваются аргументы функции test, не указан, но здесь проблема не в этом: оба выражения изменяют одну и ту же переменную, и между вычислением аргумента функции нет точки последовательности. Таким образом, не имеет значения, как вы достигаете побочных эффектов в выражениях, используемых для аргументов, вы вызываете неопределенное поведение. Компилятор может сгенерировать код, но ракета может взорваться при взлете.

person chqrlie    schedule 08.05.2015
comment
Порядок оценки не указан, а не не определен, это множественная модификация в точке следования, которая не определена. - person Shafik Yaghmour; 08.05.2015