Неопределенное, неопределенное и определяемое реализацией поведение

Mar 08 2010

Что такое неопределенное поведение в C и C ++? А как насчет неопределенного поведения и поведения, определяемого реализацией? В чем разница между ними?

Ответы

423 fredoverflow Nov 05 2010 at 17:41

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

Давайте посмотрим на классический пример:

#include <iostream>

int main()
{
    char* p = "hello!\n";   // yes I know, deprecated conversion
    p[0] = 'y';
    p[5] = 'w';
    std::cout << p;
}

Переменная pуказывает на строковый литерал "hello!\n", и два присваивания ниже пытаются изменить этот строковый литерал. Что делает эта программа? Согласно параграфу 11 раздела 2.14.5 стандарта C ++, он вызывает неопределенное поведение :

Эффект от попытки изменить строковый литерал не определен.

Я слышу, как люди кричат: «Но подождите, я могу скомпилировать это без проблем и получить результат yellow» или «Что вы имеете в виду undefined, строковые литералы хранятся в постоянной памяти, поэтому первая попытка присваивания приводит к дампу ядра». Это как раз проблема неопределенного поведения. По сути, стандарт разрешает что угодно случаться, когда вы вызываете неопределенное поведение (даже носовые демоны). Если существует «правильное» поведение в соответствии с вашей ментальной моделью языка, эта модель просто неверна; Стандарт C ++ имеет единственное право голоса, точка.

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

В разделе 1.9 стандарта C ++ также упоминаются два менее опасных брата неопределенного поведения: неопределенное поведение и поведение, определяемое реализацией :

Семантические описания в этом международном стандарте определяют параметризованную недетерминированную абстрактную машину.

Некоторые аспекты и операции абстрактной машины описаны в этом международном стандарте как определяемые реализацией (например, sizeof(int)). Они составляют параметры абстрактной машины. Каждая реализация должна включать документацию, описывающую ее характеристики и поведение в этих отношениях.

Некоторые другие аспекты и операции абстрактной машины описаны в этом международном стандарте как неопределенные (например, порядок оценки аргументов функции). По возможности, этот международный стандарт определяет набор допустимого поведения. Они определяют недетерминированные аспекты абстрактной машины.

Некоторые другие операции описаны в этом международном стандарте как неопределенные (например, эффект разыменования нулевого указателя). [ Примечание : этот международный стандарт не предъявляет требований к поведению программ, которые содержат неопределенное поведение. - конец примечания ]

В частности, в разделе 1.3.24 говорится:

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

Что вы можете сделать, чтобы избежать неопределенного поведения? По сути, вы должны читать хорошие книги по C ++ от авторов, которые знают, о чем они говорят. Винтовые интернет-уроки. К черту буллшильдт.

98 AnT Mar 08 2010 at 04:15

Ну это по сути прямая копипаста из стандартной

3.4.1 1 поведение, определяемое реализацией: Неопределенное поведение, при котором каждая реализация документирует, как делается выбор.

2 ПРИМЕР Примером определяемого реализацией поведения является распространение старшего бита, когда целое число со знаком сдвигается вправо.

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

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

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

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

2 ПРИМЕР Примером неопределенного поведения является порядок, в котором оцениваются аргументы функции.

60 AraK Mar 08 2010 at 04:28

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

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

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

неопределенное поведение
Какая из этих двух функций выполняется первой?

void fun(int n, int m);

int fun1()
{
  cout << "fun1";
  return 1;
}
int fun2()
{
  cout << "fun2";
  return 2;
}
...
fun(fun1(), fun2()); // which one is executed first?

В языке не указывается оценка слева направо или справа налево! Таким образом, неопределенное поведение может или не может привести к неопределенному поведению, но определенно ваша программа не должна вызывать неопределенное поведение.


@eSKay Я думаю, что ваш вопрос стоит отредактировать ответ, чтобы прояснить больше :)

ведь fun(fun1(), fun2());поведение «не определяется реализацией»? Ведь компилятор должен выбрать тот или иной курс?

Разница между определенным реализацией и неуказанным состоит в том, что компилятор должен выбирать поведение в первом случае, но не во втором случае. Например, реализация должна иметь одно и только одно определение sizeof(int). Таким образом, нельзя сказать, что sizeof(int)это 4 для одной части программы и 8 для других. В отличие от неопределенного поведения, когда компилятор может сказать «ОК», я буду оценивать эти аргументы слева направо, а аргументы следующей функции оцениваются справа налево. Это может произойти в одной программе, поэтому и называется неопределенным . Фактически, C ++ можно было бы упростить, если бы были указаны некоторые из неопределенных вариантов поведения. Взгляните на ответ доктора Страуструпа по этому поводу :

Утверждается, что разница между тем, что может быть создано, предоставляя компилятору эту свободу, и требующим «обычного вычисления слева направо», может быть значительной. Я не убежден, но с бесчисленными компиляторами, которые «там» пользуются этой свободой, и некоторыми людьми, страстно защищающими эту свободу, изменение будет трудным и может занять десятилетия, чтобы проникнуть в далекие уголки миров C и C ++. Я разочарован тем, что не все компиляторы предупреждают о таком коде, как ++ i + i ++. Точно так же не указан порядок оценки аргументов.

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

27 JohannesSchaub-litb Jan 24 2013 at 01:46

Из официального документа C Rationale Document

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

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

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

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

10 AndersAbel Mar 08 2010 at 04:18

Undefined Behavior vs. Unspecified Behavior содержит краткое описание этого.

Их окончательное резюме:

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

8 supercat Apr 16 2015 at 10:32

Исторически сложилось так, что и поведение, определяемое реализацией, и неопределенное поведение представляли ситуации, в которых авторы Стандарта ожидали, что люди, пишущие качественные реализации, будут использовать суждения, чтобы решить, какие поведенческие гарантии, если таковые имеются, будут полезны для программ в предполагаемом поле приложения, работающего на намеченные цели. Потребности высокопроизводительного кода для обработки чисел сильно отличаются от требований низкоуровневого системного кода, и как UB, так и IDB дают разработчикам компиляторов гибкость для удовлетворения этих различных потребностей. Ни одна из категорий не требует, чтобы реализации велись таким образом, чтобы это было полезно для какой-либо конкретной цели или даже для какой-либо цели. Однако качественные реализации, которые заявляют, что подходят для определенной цели, должны вести себя таким образом, чтобы соответствовать этой цели, независимо от того, требует этого Стандарт или нет .

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

К сожалению, с середины 1990-х годов авторы компиляторов начали интерпретировать отсутствие поведенческих мандатов как суждение о том, что поведенческие гарантии не стоят затрат даже в тех областях приложения, где они жизненно важны, и даже в системах, где они практически ничего не стоят. Вместо того, чтобы рассматривать UB как приглашение к разумному суждению, авторы компиляторов начали рассматривать его как оправдание, чтобы этого не делать.

Например, учитывая следующий код:

int scaled_velocity(int v, unsigned char pow)
{
  if (v > 250)
    v = 250;
  if (v < -250)
    v = -250;
  return v << pow;
}

реализация с дополнением до двух не должна прилагать каких-либо усилий для обработки выражения v << powкак сдвига с дополнением до двух, независимо от того, vбыло ли оно положительным или отрицательным.

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

7 SurajKThomas Mar 17 2015 at 14:11

Реализация определена

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

Неопределенные -

То же, что и определено реализацией, но не задокументировано

Неопределенный

Все может случиться, позаботьтесь об этом.

6 4pie0 May 10 2014 at 19:35

Стандарт C ++ n3337 § 1.3.10 поведение, определяемое реализацией

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

Иногда Стандарт C ++ не навязывает определенное поведение некоторым конструкциям, а вместо этого говорит, что конкретное, четко определенное поведение должно быть выбрано и описано конкретной реализацией (версией библиотеки). Таким образом, пользователь может точно знать, как будет вести себя программа, даже если Стандарт не описывает этого.


Стандарт С ++ n3337 § 1.3.24 неопределенное поведение

поведение, для которого данный международный стандарт не налагает требований [Примечание: неопределенное поведение может ожидаться, если в этом международном стандарте отсутствует какое-либо явное определение поведения или когда программа использует ошибочную конструкцию или ошибочные данные. Допустимое неопределенное поведение варьируется от полного игнорирования ситуации с непредсказуемыми результатами до поведения во время трансляции или выполнения программы задокументированным образом, характерным для среды (с выдачей диагностического сообщения или без него), до прекращения трансляции или выполнения (с выдачей диагностического сообщения). Многие ошибочные программные конструкции не приводят к неопределенному поведению; они должны быть диагностированы. - конец примечания]

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


Стандарт С ++ n3337 § 1.3.25 неопределенное поведение

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

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