Утверждение (разработка программного обеспечения) - Assertion (software development)

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

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

Подробности

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

Икс = 1;утверждать Икс > 0;Икс++;утверждать Икс > 1;

Программисты могут использовать утверждения, чтобы определять программы и рассуждать о правильности программы. Например, предварительное условие - утверждение, помещенное в начало раздела кода, - определяет набор состояний, при которых программист ожидает выполнения кода. А постусловие - помещен в конец - описывает ожидаемое состояние в конце выполнения. Например: х> 0 {х ++} х> 1.

В приведенном выше примере используются обозначения для включения утверждений, используемые К. А. Р. Хоар в своей статье 1969 года.[1] Эта нотация не может использоваться в существующих основных языках программирования. Однако программисты могут включать непроверенные утверждения, используя функция комментариев своего языка программирования. Например, в C:

Икс = 5;Икс = Икс + 1;// {x> 1}

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

Библиотеки также могут предоставлять функции утверждений. Например, в C с использованием glibc с поддержкой C99:

#включают <assert.h>int ж(пустота){    int Икс = 5;    Икс = Икс + 1;    утверждать(Икс > 1);}

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

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

использование

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

Утверждения в дизайне по контракту

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

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

Утверждения для проверки во время выполнения

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

 int общий = countNumberOfUsers(); если (общий % 2 == 0) {     // итог четный } еще {     // сумма нечетная и неотрицательная     утверждать общий % 2 == 1; }

В Ява, % это остаток оператор (по модулю ), а в Java, если его первый операнд отрицательный, результат также может быть отрицательным (в отличие от модуля, используемого в математике). Здесь программист предположил, что общий неотрицательно, так что остаток от деления на 2 всегда будет 0 или 1. Утверждение делает это предположение явным: если countNumberOfUsers действительно возвращает отрицательное значение, программа может иметь ошибку.

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

Утверждения также иногда помещаются в точки, которых выполнение не должно достигать. Например, утверждения могут быть помещены в дефолт пункт выключатель заявление на таких языках, как C, C ++, и Ява. Любой случай, который программист не обрабатывает намеренно, вызовет ошибку, и программа будет прервана, а не продолжит работу в ошибочном состоянии. В D такое утверждение добавляется автоматически, когда выключатель заявление не содержит дефолт пункт.

В Ява, утверждения были частью языка начиная с версии 1.4. Ошибки утверждения приводят к возникновению AssertionError когда программа запускается с соответствующими флагами, без которых утверждения assert игнорируются. В C, они добавляются стандартным заголовком assert.h определение утверждать (утверждение) как макрос, который сигнализирует об ошибке в случае сбоя, обычно завершая программу. В C ++, обе assert.h и кассерт заголовки предоставляют утверждать макрос.

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

Конструкции утверждений в языке позволяют легко разработка через тестирование (TDD) без использования сторонней библиотеки.

Утверждения во время цикла разработки

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

Утверждения в производственной среде

Когда программа развертывается в производство, утверждения обычно отключены, чтобы избежать накладных расходов или побочных эффектов, которые они могут иметь. В некоторых случаях утверждения полностью отсутствуют в развернутом коде, например, в утверждениях C / C ++ через макросы. В других случаях, таких как Java, утверждения присутствуют в развернутом коде и могут быть включены в поле для отладки.[2]

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

Статические утверждения

Утверждения, которые проверяются во время компиляции, называются статическими утверждениями.

Статические утверждения особенно полезны во время компиляции метапрограммирование шаблона, но также может использоваться в языках низкого уровня, таких как C, путем введения недопустимого кода, если (и только если) утверждение не выполняется. C11 и C ++ 11 поддерживать статические утверждения напрямую через static_assert. В более ранних версиях C статическое утверждение может быть реализовано, например, следующим образом:

#define SASSERT (пред) переключатель (0) {case 0: case pred :;}SASSERT( BOOLEAN УСЛОВИЕ );

Если (БУЛЕВОЕ УСЛОВИЕ) часть оценивается как ложь, то приведенный выше код не будет компилироваться, потому что компилятор не разрешит два ярлыки коробки с той же константой. Логическое выражение должно быть постоянным значением времени компиляции, например (размер (число) == 4) было бы допустимым выражением в этом контексте. Эта конструкция не работает в области видимости файла (т.е. не внутри функции), поэтому она должна быть заключена в функцию.

Еще один популярный[3] способ реализации утверждений в C:

статический char const static_assertion[ (BOOLEAN УСЛОВИЕ)                                    ? 1 : -1                                  ] = {'!'};

Если (БУЛЕВОЕ УСЛОВИЕ) part оценивается как false, то приведенный выше код не будет компилироваться, потому что массивы могут не иметь отрицательной длины. Если на самом деле компилятор допускает отрицательную длину, то байт инициализации ( '!' part) должен вызвать недовольство даже у таких излишне снисходительных компиляторов. Логическое выражение должно быть постоянным значением времени компиляции, например (размер (число) == 4) было бы допустимым выражением в этом контексте.

Оба эти метода требуют создания уникальных имен. Современные компиляторы поддерживают __ПРИЛАВОК__ определение препроцессора, которое облегчает построение уникальных имен, возвращая монотонно возрастающие числа для каждой единицы компиляции.[4]

D предоставляет статические утверждения с помощью статическое утверждение.[5]

Отключение утверждений

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

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

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

Сравнение с обработкой ошибок

Утверждения отличаются от стандартной обработки ошибок. Утверждения документируют логически невозможные ситуации и обнаруживают ошибки программирования: если невозможное происходит, значит, что-то фундаментальное не так с программой. Это отличается от обработки ошибок: возможно большинство ошибок, хотя некоторые из них могут быть крайне маловероятными на практике. Использование утверждений в качестве универсального механизма обработки ошибок неразумно: утверждения не позволяют исправить ошибки; сбой утверждения обычно резко останавливает выполнение программы; а утверждения часто отключены в производственном коде. Утверждения также не отображают удобное сообщение об ошибке.

Рассмотрим следующий пример использования утверждения для обработки ошибки:

  int *ptr = маллок(размер(int) * 10);  утверждать(ptr);  // используем ptr  ...

Здесь программист знает, что маллок вернет НОЛЬ указатель если память не выделена. Это возможно: операционная система не гарантирует, что каждый вызов маллок преуспеет. При возникновении ошибки нехватки памяти программа немедленно прерывается. Без утверждения программа продолжит работу до тех пор, пока ptr был разыменован и, возможно, дольше, в зависимости от конкретного используемого оборудования. Пока утверждения не отключены, немедленный выход гарантирован. Но если требуется постепенный сбой, программа должна обработать сбой. Например, у сервера может быть несколько клиентов, или он может содержать ресурсы, которые не будут освобождены чисто, или у него могут быть незафиксированные изменения для записи в хранилище данных. В таких случаях лучше завершить одну транзакцию, чем резко прервать ее.

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

Рассмотрим другую версию предыдущего примера:

  int *ptr;  // Заявление ниже не выполняется, если malloc () возвращает NULL,  // но не выполняется вообще при компиляции с -NDEBUG!  утверждать(ptr = маллок(размер(int) * 10));  // использовать ptr: ptr не инициализируется при компиляции с -NDEBUG!  ...

Это может показаться разумным способом присвоить возвращаемое значение маллок к ptr и проверьте, есть ли это НОЛЬ за один шаг, но маллок звонок и назначение на ptr является побочным эффектом оценки выражения, которое формирует утверждать условие. Когда NDEBUG параметр передается компилятору, так как, когда программа считается исправной и выпущенной, утверждать() заявление удалено, поэтому malloc () не называется, рендеринг ptr неинициализированный. Это потенциально может привести к ошибка сегментации или похожие нулевой указатель ошибка гораздо ниже по строке выполнения программы, вызывая ошибки, которые могут быть спорадический и / или их сложно отследить. Программисты иногда используют аналогичное определение VERIFY (X), чтобы решить эту проблему.

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

История

В 1947 г. фон Нейман и Голдстайн[7] на их дизайн для Машина IAS, они описали алгоритмы, используя раннюю версию блок-схемы, в котором они включали утверждения: «Может быть верно, что всякий раз, когда C действительно достигает определенной точки на блок-схеме, одна или несколько связанных переменных обязательно будут обладать определенными заданными значениями, или обладать определенными свойствами, или удовлетворять определенным свойствам друг с другом. Кроме того, в такой момент мы можем указать действительность этих ограничений. По этой причине мы будем обозначать каждую область, в которой утверждается действительность таких ограничений, специальным окном, которое мы называем окном утверждения ".

Утвержденный метод доказательства правильности программ был защищен Алан Тьюринг. В своем выступлении «Проверка большой процедуры» в Кембридже 24 июня 1949 г. Тьюринг предложил: «Как можно проверить большую процедуру в том смысле, что она верна? Для того, чтобы человек, который проверяет, не имел слишком сложных проблем. задача, программист должен сделать ряд определенных утверждения которые можно проверить индивидуально и из которых легко следует правильность всей программы ».[8]

Смотрите также

Рекомендации

внешняя ссылка