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

Когда мы должны использовать утверждения в C?

Я пишу функцию в C. В качестве стиля, когда полезно использовать assert по сравнению с возвратом кода ошибки. Допустим, функция делит два числа. Должен ли я утверждать, что делитель не равен нулю или мне нужно вернуть код ошибки? Пожалуйста, дайте больше примеров, если сможете, чтобы сделать различие понятным.

4b9b3361

Ответ 1

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

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

void sort(int *a, size_t n)
{
    recursive_super_duper_sort(a, 0, n);
    assert(is_sorted(a, n));
}

static bool is_sorted(int const *a, size_t n)
{
    for (size_t i=0; i<n-1; i++)
        if (a[i] > a[i+1])
            return false;

    return true;
}

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

Ответ 2

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

Это две совершенно разные вещи с разными приложениями.

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

Ответ 3

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

Ответ 4

Это действительно вопрос вкуса. Вот мое мнение.

Основное правило: ошибка утверждения всегда является ошибкой в ​​программе.

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

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

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

Ответ 5

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

Я слышал, как Джим Коплиен (общий гуру С++ и тренер SCRUM) защищает ваши утверждения активным в развернутом коде. (Это звучит сумасшедшим, я знаю...) Это было специально для высокосерверного кода сервера. Мотивация заключалась в том, что лучше потерпеть неудачу, тяжело и позволить другому node взять на себя ответственность, чем терпеть ваш сервер в "невозможном" состоянии.

(И, конечно, отслеживать ошибки и анализировать их. Это означает, что есть ошибка или неправильное предположение.)

Ответ 6

Во-первых, assert из заголовка <assert.h> можно отключить (например, скомпилировать с помощью gcc -DNDEBUG), а иногда и для "производственной" версии двоичного файла.

Во-вторых, как указано на странице руководства Linux,

   The  purpose  of  this macro is to help the programmer find bugs in his
   program.   The  message  "assertion  failed  in  file  foo.c,  function
   do_bar(), line 1287" is of no help at all to a user.

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

Некоторые инструменты (или даже компиляторы) могут использовать assert -ions, например. оптимизируйте свой код.

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

Ответ 7

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

Для операций с плавающей запятой вы можете использовать NaN для сообщения об ошибках. Для целых операций код ошибки - это просто ваш единственный вариант.

Ответ 8

Используйте assert, когда ваша программа соответствует ситуации, которая не позволяет продолжить. Утверждения - это "контракт", и я использую их как "контракт" с ОС и "опасность в задержке".

Чтобы эмулировать исключения, вы все равно можете использовать GOTO 'ERRORLABEL' и завершить очистку после запуска функции очистки.

Ответ 9

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

У меня было два параллельных массива - пусть они называются a и b - и я собирался запустить индекс i от 0 до размера a, а затем сделать что-то с a[i] и b[i]. Но если бы в b было меньше элементов, чем в a, мой код имел бы нарушение границ массива в b. Но другие части кода должны сохранять размеры a и b идентичными. Поэтому я поставил утверждение перед циклом, утверждая, что размеры a и b равны.

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