Подтвердить что ты не робот

Где следует утверждать() в C соотв. С++?

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

4b9b3361

Ответ 1

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

Принцип.

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

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

Пример:

char const* strstr(char const* haystack, char const* needle) {
  assert(haystack); assert(needle);

  // ...
}

Альтернативы.

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

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

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

Где НЕ использовать.

Во-первых, следует понимать, что assert всегда полезен в коде отладки. В Release определяется NDEBUG и не генерируется код. Как следствие, в Release assert имеет то же значение, что и комментарий.

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

Во-вторых, следует понимать, что неправильный ввод является частью вашей жизни. Вы хотите, чтобы ваш компилятор отображал сообщение assert каждый раз, когда вы делали ошибку? Hum! Поэтому:

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

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

  • Никогда не оставляйте утверждения в Release.

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

Где его использовать.

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

  • Используйте его во время циклов разработки и тестирования.

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

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

И в выпуске?

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

В развернутом программном обеспечении лучшие практики, которые я видел, подразумевают создание дампа памяти и передачу его обратно разработчикам для анализа при попытке не потерять какие-либо данные пользователя и вести себя как можно более любезно к несчастному пользователю. Я чувствую себя действительно счастливым, чтобы работать на стороне сервера, когда я рассматриваю трудности этой задачи;)

Ответ 2

Я выкину свой взгляд на assert(). Я могу найти, что assert() делает в другом месте, но stackoverflow обеспечивает хороший форум для предложений о том, как и когда его использовать.

Оба assert и static_assert выполняют аналогичные функции. Скажем, у вас есть функция foo. Например, скажем, у вас есть функция foo(void*), которая предполагает, что ее аргумент не равен null:

void foo(void* p) {
  assert(p);
  ...
}

В вашей функции есть пара людей, которые это волнуют.

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

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

Вы должны использовать assert для кодирования этой информации всякий раз, когда это практично. Мне нравится думать об этом как о том, что "на данный момент в коде это верно" (и это говорит об этом способом , поэтому намного сильнее комментария). Конечно, если такое выражение фактически не передает много/никакой информации, тогда оно не требуется.

Ответ 3

Я думаю, что есть простой и мощный момент:

assert () предназначен для проверки внутренней согласованности.

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

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

  • new int в порядке до своих предварительных условий, поэтому, если память недоступна, бросание является разумным ответом только. (Постусловие malloc является "допустимым указателем или NULL" )
  • Постусловие конструктора - это существование объекта, инварианты которого установлены. Если он не может построить допустимое состояние, метарование - это разумный ответ только.

assert не следует использовать для вышеуказанного. Напротив,

void sort (int * begin, int * end) {
    // assert (begin <= end); // OPTIONAL precondition, possibly want to throw
    for (int * i = begin, i < end; ++i) {
        assert (is_sorted (begin, i)); // invariant
        // insert *i into sorted position ...
    }
}

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

Короче говоря: assert для вещей, которые НИКОГДА не будут, если программа ЛОКАЛЬНО правильная, исключения для вещей, которые могут пойти не так, даже если код верен.

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

Ответ 4

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

void my_func( char* str )
{
   assert ( str != NULL );
   /* code */
}

Он также может использоваться с функциями, возвращающими указатель NULL при сбое:

SDL_Surface* screen = SDL_SetVideoMode( 640, 480, 16, SDL_HWSURFACE );
assert ( screen != NULL );

Точное сообщение об ошибке assert() дает, зависит от вашего компилятора, но обычно идет по этим строкам:

Assertion failed: str, mysrc.c, line 5