2017-02-22 7 views
1

Вот что я имею в виду: i++ + i++ не определено, и поэтому внеочередные записи записываются в массив.Являются ли некоторые неопределенные поведения более неопределенными, чем другие?

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

Для i++ + i++, однако, история кажется другой. Скажем, компилятор генерирует что-то. Не знаю, что именно. Очень неопределенно. На самом деле, это так неопределенно, что мы привыкли слышать, что кошки могут забеременеть (хотя совсем недавно - с CppCon 2016, я думаю - люди начали понимать, что неопределенное поведение не может получить кошку, беременную, в конце концов).

Однако, как только мы открываем коробку и см, что компилятор сгенерированный, и это не годная для использования коды (инъекции, гонки данных и т.д. - например, компилятор решил бросить i++ + i++ прочь вообще), ISN» t это именно то, что будет выполнено - разве это не определено с этой точки на?

Другими словами, этот последний случай - это то, что мы можем назвать время неопределенного времени компиляции. В условиях кошки он похож на Schrödinger's cat, состояние которого неизвестно , пока вы не откроете коробку (см. Сгенерированную сборку), после чего вы увидите фактическую реальность, которая будет выполнена. (Интересно, если неопределенное поведение сделать отравлен мертвый кот беременна.)

Конечно, неопределенного поведения является правового термина означал для стандарта. Речь идет о «поведении», которое происходит в действительности.

+0

С точки зрения стандарта C++ все это не имеет значения. – jotik

+0

«Не совсем ли это определено на этой точке» - НЕТ, вы не можете вернуть кошку в поле –

+1

Не определено ... ну, «неопределено» для стандарта языка C++. Конечно, компиляторы и реализации библиотек могут реализовать четко определенное поведение в некоторых или большинстве случаев, но это формально неопределенное поведение для стандарта. – roalz

ответ

5

«Неопределенное поведение» - это термин, который применяется к стандартам . Если в стандарте указано, что поведение не определено (или не определяет его, прямо или косвенно), то реализация стандарта может принять любое действие, которое его авторы хотят, или позволить вещам идти своим путем, и привести к помету котят, если поэтому судьба решила. С точки зрения стандарта это двоичный, определенный/неопределенный, не определенный «больше» или «меньше».

Теперь, помимо стандартов, у вас есть реальность:

Так что - если поведение не определено стандартом, умный, дружелюбный компилятор выдаст ошибку или предупреждение (или ошибки во время выполнения и во время компиляции предупреждение). Это разрешено; ведь ничто не запрещает такую ​​реакцию!

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

Кроме того, если реакция определенно имеет смысл и «экономит день», делая то, что хотел автор в 99.999% случаях - другие авторы, скорее всего, тоже начнут ее реализовывать (даже если это намек на исправление в предупреждающее сообщение), и оно может в конечном итоге войти в стандарт.

И последнее, но не менее важное: компилятор применит синтаксические правила, соответствующие отдельным частям, и произведет что-то, вероятно, что-то, что не имеет большого смысла. Это вряд ли приведет к появлению котят, и вы абсолютно не гарантируете, что результат будет повторяемым, но обычно он повторяется. Скажем, при вызове «delete» на указателе на то, что не было создано с помощью «новой», всегда будет возникать ошибка сегментации. Но это «единственная» практика и не гарантируется никакими средствами.

Вы вряд ли на самом деле столкнуться программным обеспечением, которое будет выходить из своего пути, чтобы вызвать определенно нежелательные или абсолютно несвязанные результатов непредсказуемого поведение, так что не SPAY вашей кошки только пока, но стандарт Безразлично» t запретить это. Если у автора вашего программного обеспечения было действительно искривленное чувство юмора, вы можете столкнуться с чем-то более творческим. Например, система PAM в Unix, столкнувшись с отсутствием файла passwd (все определения учетных записей пользователей), сообщая вам о логине «Вы не существуете. Уходите».

+0

Для многих видов поведения компилятор, который утверждает, что подходит для какой-либо цели (например, системное программирование), будет стремиться вести себя так, как того требует стандарт, независимо от того, требует ли это стандарт или нет, особенно если в стандарте не указывается любые способы достижения поведения (поскольку некоторые поля приложений не потребуют этого, и не все платформы могут его предоставить), но его можно легко поддерживать, используя угловой случай другого поведения, которое определено.Например, на большинстве платформ, если бы программа определяла поведение без «изменчивого» классификатора на некотором ... – supercat

+0

... переменной, наличие «изменчивого» квалификатора в переменной не повлияло бы на поведение в тех случаях, когда на самом деле доступ к хранилищу не осуществлялся внешними средствами (это может, конечно, повлиять на оптимизацию). Если библиотека объявляет переменную 'volatile' исключительно потому, что некоторые клиенты могут обращаться к ней за пределами контроля компилятора, клиент, который не будет обращаться к ней таким образом, должен иметь доступ к ней с помощью кода, который использует неквалифицированные указатели , при условии, что квалификатор отбрасывается, когда это необходимо. Такие клиенты терпят неудачу, если переменная * была * доступна внешними средствами, ... – supercat

+0

... но использование библиотекой 'volatile' для размещения клиентов, которые в ней нуждаются, не должно налагать существенное бремя на клиентов, которые не Мне нужен этот определитель. – supercat

2

Значение «неопределенного» в стандарте C++ - это поведение, «такое, которое может возникнуть при использовании ошибочной конструктивной программы или ошибочных данных, для которых настоящий международный стандарт не предъявляет никаких требований». (Эта цитата прямо из ISO/IEC 14882 - стандарт C++ 1998 года).

В этом определении не предусмотрены понятия «больше» или «меньше». Если стандарт накладывает одно или несколько требований на требуемое поведение, поведение не определено.

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

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

-2

"см. Сгенерированную сборку, в этот момент вы видите фактическую реальность, которая будет выполнена".

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

(Конечно, проблема остановки также существует в четко определенной C++, но не самомодифицирующийся C++.)

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

+0

О, великие, нисходящие, которые не понимают ответа, но также не требуют объяснений. – MSalters

+0

Ошибка в вашем ответе заключается в рассмотрении поведения компьютера после компиляции в виде черного ящика. Детерминированный компьютер будет полностью повторять один и тот же машинный код, даже если код является бессмысленным - и на реальном (не полностью детерминированном) компьютере, при уровне выполнения машинного кода, гораздо меньшее число вариантов поведения не определено. Если сгенерированный машинный код не вызывает никакого UB, как определено стандартом CPU, его поведение будет полностью детерминированным. И вы можете определить, делает он это или нет, анализируя код сборки. –

+0

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

1

Если какое-либо действие вызывает поведение определяется реализацией, что означает

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

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

В противоположность этому, если действие вызывает неопределенное поведение, это означает, что

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

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

Некоторые компиляторы не интерпретируют «Неопределенное поведение» как приглашение судить, а вместо этого указывают на то, что суждения не требуются. Однако, когда был опубликован C89, термин широко понимался как имеющий значение, которое отличалось от исполнения, обусловленного реализацией, только в тех случаях, когда определение конкретного поведения было бы непрактичным; Я не видел никаких указаний на то, что последующие стандарты должны были изменить это.

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