Книга: Программирование для Linux. Профессиональный подход

2.2.1. Макрос assert()

закрыть рекламу

2.2.1. Макрос assert()

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

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

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

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

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

Предположим, к примеру, что в цикле вызывается функция do_something(). В случае успешного выполнения она возвращает 0, иначе — ненулевое значение. Легкомысленный программист считает, что функция всегда завершается успешно, поэтому возникает соблазн написать так:

for (i =0; i < 100; ++i)
 assert(do_something() == 0);

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

for (i = 0; i < 100; ++i) {
 int status = do_something();
 assert(status == 0);
}

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

Дадим несколько полезных советов.

? Проверяйте наличие пустых указателей, например в списке аргументов функции. Сообщение об ошибке, генерируемое строкой {assert (pointer != NULL)},

Assertion 'pointer != ((void *)0)' failed.

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

Segmentation fault (core dumped)

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

assert(foo > 0);

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

Оглавление книги


Генерация: 1.050. Запросов К БД/Cache: 4 / 0
поделиться
Вверх Вниз