~~~~~~~~~~~~~~~~~~~~ Arc.Ask3.Ru ~~~~~~~~~~~~~~~~~~~~~ 
Номер скриншота №:
✰ 9E224754AF7D2D471EEB3188FEA347A3__1717606800 ✰
Заголовок документа оригинал.:
✰ Undefined behavior - Wikipedia ✰
Заголовок документа перевод.:
✰ Неопределенное поведение — Википедия ✰
Снимок документа находящегося по адресу (URL):
✰ https://en.wikipedia.org/wiki/Undefined_behavior ✰
Адрес хранения снимка оригинал (URL):
✰ https://arc.ask3.ru/arc/aa/9e/a3/9e224754af7d2d471eeb3188fea347a3.html ✰
Адрес хранения снимка перевод (URL):
✰ https://arc.ask3.ru/arc/aa/9e/a3/9e224754af7d2d471eeb3188fea347a3__translat.html ✰
Дата и время сохранения документа:
✰ 21.06.2024 10:07:38 (GMT+3, MSK) ✰
Дата и время изменения документа (по данным источника):
✰ 5 June 2024, at 20:00 (UTC). ✰ 

~~~~~~~~~~~~~~~~~~~~~~ Ask3.Ru ~~~~~~~~~~~~~~~~~~~~~~ 
Сервисы Ask3.ru: 
 Архив документов (Снимки документов, в формате HTML, PDF, PNG - подписанные ЭЦП, доказывающие существование документа в момент подписи. Перевод сохраненных документов на русский язык.)https://arc.ask3.ruОтветы на вопросы (Сервис ответов на вопросы, в основном, научной направленности)https://ask3.ru/answer2questionТоварный сопоставитель (Сервис сравнения и выбора товаров) ✰✰
✰ https://ask3.ru/product2collationПартнерыhttps://comrades.ask3.ru


Совет. Чтобы искать на странице, нажмите Ctrl+F или ⌘-F (для MacOS) и введите запрос в поле поиска.
Arc.Ask3.ru: далее начало оригинального документа

Неопределенное поведение — Википедия Jump to content

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

Из Википедии, бесплатной энциклопедии

В компьютерном программировании неопределенное поведение ( UB ) является результатом выполнения программы, поведение которой предписано быть непредсказуемым в спецификации языка , которой соответствует компьютерный код . Это отличается от неопределенного поведения , для которого спецификация языка не предписывает результат, и поведения, определяемого реализацией, которое зависит от документации другого компонента платформы ( например, ABI или документации переводчика ).

В сообществе программистов на языке неопределенное поведение можно с юмором назвать « носовыми демонами » после публикации на comp.std.c , в которой неопределенное поведение объясняется как разрешение компилятору делать все, что он пожелает, даже «заставлять демонов вылетать из вашего компьютера». нос". [1]

Обзор [ править ]

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

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

Однако прогрессивная стандартизация платформ сделала это преимущество меньше, особенно в новых версиях C. Теперь случаи неопределенного поведения обычно представляют собой однозначные ошибки в коде, например, индексацию массива за его пределами. По определению, среда выполнения может предполагать, что неопределенное поведение никогда не происходит; поэтому некоторые недопустимые условия не нуждаются в проверке. Для компилятора это также означает, что различные преобразования программы становятся действительными или доказательства их корректности упрощаются; это позволяет проводить различные виды оптимизации, корректность которых зависит от предположения, что состояние программы никогда не удовлетворяет никакому такому условию. Компилятор также может удалять явные проверки, которые могли быть в исходном коде, не уведомляя программиста; например, обнаружение неопределенного поведения путем проверки того, произошло ли оно, по определению не гарантирует работу. Это затрудняет или делает невозможным программирование портативной отказоустойчивой опции (для некоторых конструкций возможны непереносимые решения).

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

Для C и C++ в этих случаях компилятору разрешено выдавать диагностику во время компиляции, но это не обязательно: реализация будет считаться правильной, что бы она ни делала в таких случаях, аналогично терминам безразличия в цифровой логике. . Программист несет ответственность за написание кода, который никогда не вызывает неопределенное поведение, хотя реализации компилятора могут выдавать диагностику, когда это происходит. В настоящее время компиляторы имеют флаги, позволяющие такую ​​диагностику, например: -fsanitize=undefined включает «дезинфицирующее средство неопределенного поведения» ( UBSan ) в gcc 4.9 [2] и в лязге . Однако этот флаг не установлен по умолчанию, и его включение — выбор человека, создающего код.

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

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

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

Преимущества [ править ]

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

Пример для языка C:

int   foo  (  unsigned   char   x  ) 
 { 
      int   value   =   2147483600  ;     /* предполагая, что 32-битное целое число и 8-битное char */ 
      value   +=   x  ; 
       if   (  значение   <   2147483600  ) 
         бар  (); 
       возвращаемое   значение  ; 
  } 

Значение x не может быть отрицательным, и, учитывая, что переполнение целого числа со знаком является неопределенным поведением в C, компилятор может предположить, что value < 2147483600всегда будет ложным. Таким образом if оператор, включая вызов функции bar, может быть проигнорирован компилятором, поскольку тестовое выражение в ifне имеет побочных эффектов и его состояние никогда не будет удовлетворительным. Таким образом, код семантически эквивалентен:

int   foo  (  unsigned   char   x  ) 
 { 
      int   value   =   2147483600  ; 
       значение   +=   х  ; 
       возвращаемое   значение  ; 
  } 

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

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

void   run_tasks  (  unsigned   char   *  ptrx  )   { 
     int   z  ; 
      z   =   foo  (  *  ptrx  ); 
      while   (  *  ptrx   >   60  )   { 
         run_one_task  (  ptrx  ,   z  ); 
      } 
 } 

Компилятор волен оптимизировать while-цикл здесь, применив анализ диапазона значений : проверив foo(), он знает, что начальное значение, на которое указывает ptrx не может превышать 47 (так как любое большее число вызовет неопределенное поведение в foo()); поэтому первоначальная проверка *ptrx > 60всегда будет ложным в соответствующей программе. Идем дальше, поскольку результат z теперь никогда не используется и foo() не имеет побочных эффектов, компилятор может оптимизировать run_tasks()быть пустой функцией, которая возвращается немедленно. Исчезновение while-loop может быть особенно неожиданным, если foo() определяется в отдельно скомпилированном объектном файле .

Еще одним преимуществом возможности неопределенного переполнения целочисленного знака является то, что это позволяет хранить и манипулировать значением переменной в регистре процессора , которое больше, чем размер переменной в исходном коде. Например, если тип переменной, указанный в исходном коде, уже ширины собственного регистра (например, intна 64-битной машине (обычный сценарий), то компилятор может безопасно использовать 64-битное целое число со знаком для переменной в машинном коде, который он создает, без изменения определенного поведения кода. Если бы программа зависела от поведения переполнения 32-битного целого числа, то компилятору пришлось бы вставлять дополнительную логику при компиляции для 64-битной машины, поскольку поведение переполнения большинства машинных инструкций зависит от ширины регистра. [3]

Неопределенное поведение также позволяет выполнять дополнительные проверки во время компиляции как компиляторами, так и статическим анализом программы . [ нужна цитата ]

Риски [ править ]

Стандарты C и C++ имеют несколько форм неопределенного поведения, которые обеспечивают большую свободу в реализации компилятора и проверках во время компиляции за счет неопределенного поведения во время выполнения, если оно присутствует. В частности, стандарт ISO для C имеет приложение, в котором перечислены распространенные источники неопределенного поведения. [4] Более того, компиляторам не требуется диагностировать код, который использует неопределенное поведение. Следовательно, программисты, даже опытные, часто полагаются на неопределенное поведение либо по ошибке, либо просто потому, что они плохо разбираются в правилах языка, который может занимать сотни страниц. Это может привести к ошибкам, которые проявляются при использовании другого компилятора или других настроек. Тестирование или фаззинг с включенными динамическими проверками неопределенного поведения, например дезинфицирующими средствами Clang , может помочь обнаружить неопределенное поведение, не диагностируемое компилятором или статическими анализаторами. [5]

Неопределенное поведение может привести к уязвимостям безопасности в программном обеспечении. Например, переполнение буфера и другие уязвимости безопасности в основных веб-браузерах возникают из-за неопределенного поведения. Когда CERT в 2008 году разработчики GCC изменили свой компилятор таким образом, что он пропустил некоторые проверки переполнения, основанные на неопределенном поведении, выдал предупреждение против более новых версий компилятора. [6] Linux Weekly News отметил, что такое же поведение наблюдалось в PathScale C , Microsoft Visual C++ 2005 и нескольких других компиляторах; [7] позже предупреждение было изменено, чтобы предупреждать о различных компиляторах. [8]

Примеры на C и C++ [ править ]

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

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

Попытка изменить строковый литерал приводит к неопределенному поведению: [10]

char   *  p   =   "Википедия"  ;     // действительный C, устарел в C++98/C++03, неправильно сформирован с C++11 
 p  [  0  ]   =   'W'  ;     // неопределенное поведение 

Целочисленное деление на ноль приводит к неопределенному поведению: [11]

интервал   х   =   1  ; 
  вернуть   х   /   0  ;     // неопределенное поведение 

Некоторые операции с указателями могут привести к неопределенному поведению: [12]

int   arr  [  4  ]   =   {  0  ,   1  ,   2  ,   3  }; 
  int   *  p   =   arr   +   5  ;     // неопределенное поведение при индексации за пределами границ 
 p   =   NULL  ; 
  интервал   а   =   *  п  ;           // неопределенное поведение при разыменовании нулевого указателя 

В C и C++ реляционное сравнение указателей на объекты (для сравнения «меньше» или «больше») строго определено только в том случае, если указатели указывают на члены одного и того же объекта или элементы одного и того же массива . [13] Пример:

int   main  (  void  ) 
 { 
   int   a   =   0  ; 
    интервал   б   =   0  ; 
    возврат   &  а   <   &  б  ;     /* неопределенное поведение */ 
 } 

Достижение конца функции, возвращающей значение (кроме main()) без оператора return приводит к неопределенному поведению, если значение вызова функции используется вызывающей стороной: [14]

int   f  () 
 { 
 }    /* неопределенное поведение, если используется значение вызова функции*/ 

Изменение объекта между двумя точками последовательности более одного раза приводит к неопределенному поведению. [15] Начиная с C++11, произошли значительные изменения в причинах неопределенного поведения в отношении точек последовательности. [16] Современные компиляторы могут выдавать предупреждения, когда сталкиваются с несколькими неупорядоченными модификациями одного и того же объекта. [17] [18] Следующий пример вызовет неопределенное поведение как в C, так и в C++.

int   f  (  int   i  )   { 
   return   i  ++   +   i  ++  ;     /* неопределенное поведение: две неупорядоченные модификации i */ 
 } 

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

а  [  я  ]   =   я  ++  ;     // неопределенное поведение 
 printf  (  "%d %d  \n  "  ,   ++  n  ,   power  (  2  ,   n  ));     // также неопределенное поведение 

В C/C++ побитовый сдвиг значения на число битов, которое либо является отрицательным числом, либо больше или равно общему количеству битов в этом значении, приводит к неопределенному поведению. Самый безопасный способ (независимо от производителя компилятора) — всегда сохранять количество смещаемых битов (правый операнд << и >> побитовые операторы ) в диапазоне: [ 0, sizeof value * CHAR_BIT - 1] (где value левый операнд).

интервал   число   =   -1  ; 
  беззнаковое   целое   значение   =   1   <<   число  ;     // сдвиг на отрицательное число - неопределенное поведение 

 num   =   32  ;     // или любое другое число больше 31 
 val   =   1   <<   num  ;     // литерал '1' вводится как 32-битное целое число - в этом случае сдвиг более чем на 31 бит является неопределенным поведением 

 num   =   64  ;     // или любое другое число больше 63 
 unsigned   long   long   val2   =   1ULL   <<   num  ;     // литерал '1ULL' вводится как 64-битное целое число - в этом случае сдвиг более чем на 63 бита является неопределенным поведением 

Примеры в Rust [ править ]

Хотя неопределенное поведение никогда не присутствует в безопасном Rust , в небезопасном Rust можно вызвать неопределенное поведение разными способами. [20] Например, создание недопустимой ссылки (ссылки, которая не ссылается на допустимое значение) вызывает немедленное неопределенное поведение:

fn   main  ()   { 
     // Следующая строка вызывает немедленное неопределенное поведение. 
      let   _null_reference  :  &  i32   =   unsafe   {   std  ::  mem  ::  zeroed  ()   }; 
  } 

Ссылку использовать не обязательно; неопределенное поведение вызывается просто при создании такой ссылки.

См. также [ править ]

Ссылки [ править ]

  1. ^ «носовые демоны» . Файл жаргона . Проверено 12 июня 2014 г.
  2. ^ Дезинфицирующее средство для неопределенного поведения GCC - ubsan
  3. ^ «Немного информации о компиляторах, использующих знаковое переполнение» .
  4. ^ ISO/IEC 9899:2011 §J.2.
  5. ^ Джон Регер (19 октября 2017 г.). «Неопределенное поведение в 2017 году, cppcon 2017» . YouTube .
  6. ^ «Примечание об уязвимости VU#162289 — gcc молча отменяет некоторые циклические проверки» . База данных заметок об уязвимостях . СЕРТ. 4 апреля 2008 г. Архивировано из оригинала 9 апреля 2008 г.
  7. ^ Джонатан Корбет (16 апреля 2008 г.). «GCC и переполнение указателя» . Еженедельные новости Linux .
  8. ^ «Примечание об уязвимости VU#162289 — компиляторы C могут молча отбросить некоторые циклические проверки» . База данных заметок об уязвимостях . СЕРТ. 8 октября 2008 г. [4 апреля 2008 г.].
  9. ^ Паскаль Куок и Джон Регер (4 июля 2017 г.). «Неопределенное поведение в 2017 году, встроенное в академический блог» .
  10. ^ ИСО / МЭК (2003). ISO/IEC 14882:2003(E): Языки программирования – C++ §2.13.4 Строковые литералы [lex.string] параграф. 2
  11. ^ ИСО / МЭК (2003). ISO/IEC 14882:2003(E): Языки программирования – C++ §5.6 Мультипликативные операторы [expr.mul], параграф. 4
  12. ^ ИСО / МЭК (2003). ISO/IEC 14882:2003(E): Языки программирования – C++ §5.7 Аддитивные операторы [expr.add], параграф. 5
  13. ^ ИСО / МЭК (2003). ISO/IEC 14882:2003(E): Языки программирования – C++ §5.9 Операторы отношения [expr.rel], параграф. 2
  14. ^ ИСО / МЭК (2007). ISO/IEC 9899:2007(E): Языки программирования – C §6.9 Внешние определения, параграф. 1
  15. ^ ANSI X3.159-1989 Язык программирования C , сноска 26
  16. ^ «Порядок оценки — cppreference.com» . ru.cppreference.com . Проверено 9 августа 2016 г.
  17. ^ «Параметры предупреждения (с использованием коллекции компиляторов GNU (GCC))» . GCC, Коллекция компиляторов GNU — Проект GNU — Фонд свободного программного обеспечения (FSF) . Проверено 9 июля 2021 г.
  18. ^ «Диагностические флаги в Clang» . Документация Clang 13 . Проверено 9 июля 2021 г.
  19. ^ ИСО / МЭК (1999). ISO/IEC 9899:1999(E): Языки программирования – C §6.5 Выражения, параграф. 2
  20. ^ «Поведение считается неопределенным» . Справочник по ржавчине . Проверено 28 ноября 2022 г.

Дальнейшее чтение [ править ]

Внешние ссылки [ править ]

Arc.Ask3.Ru: конец оригинального документа.
Arc.Ask3.Ru
Номер скриншота №: 9E224754AF7D2D471EEB3188FEA347A3__1717606800
URL1:https://en.wikipedia.org/wiki/Undefined_behavior
Заголовок, (Title) документа по адресу, URL1:
Undefined behavior - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть, любые претензии не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, денежную единицу можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)