Linux программирование в примерах
Операторы проверки особенно полезны для описания двух разновидностей инвариантов: предусловий и постусловий: условий, которые должны быть истинными соответственно перед и после исполнения сегмента кода. Простым примером предусловий и постусловий является линейный поиск:
/* lsearch --- возвратить индекс с данным значением в массиве или -1,если не найдено */int lsearch(int *array, size_t size, int value) {size_t i;/* предусловие: array != NULL *//* предусловие: size > 0 */for (i = 0; i < size; i++)if (array[i] == value)return i;/* постусловие: i == size */return -1;}Этот пример определяет условия, используя комментарии. Но не было бы лучше проверить условия с использованием кода? Это является задачей макроса
:assert()#include <assert.h> /* ISO С */void assert(/* <i>скалярное выражение</i> */);Когда скалярное выражение ложно, макрос
выводит диагностическое сообщение и завершает программу (с помощью функцииassert(); см. раздел 12.4 «Совершение самоубийства:abort()»).abort()снова предоставляет функциюch12-assert.c, на этот раз с оператором проверки и функциейlsearch():main()1 /* ch12-assert.с --- демонстрация операторов проверки */23 #include <stdio.h>4 #include <assert.h>56 /* lsearch --- возвращает индекс с данным значением в массиве или -1, если не найдено */78 int lsearch(int *array, size_t size, int value)9 {10 size_t i;1112 assert(array != NULL);13 assert(size > 0);14 for (i = 0; i < size; i++)15 if (array[i] == value)16 return i;1718 assert(i == size);1920 return -1;21 }2223 /* main --- проверить наши условия */2425 int main(void)26 {27 #define NELEMS 428 static int array[NELEMS] = { 1, 17, 42, 91 };29 int index;3031 index = lsearch(array, NELEMS, 21);32 assert(index == -1);3334 index = lsearch(array, NELEMS, 17);35 assert(index == 1);3637 index = lsearch(NULL, NELEMS, 10); /* won't return */3839 printf("index = %d\n", index);4041 return 0;42 }После компиляции и запуска оператор проверки в строке 12 «выстреливает»:
$ <b>ch12-assert</b> /* Запуск программы */ch12-assert: ch12-assert.c:12: lsearch: Assertion 'array != ((void *)0)' failed.Aborted (core dumped)Сообщение от
варьирует от системы к системе. Для GLIBC на GNU/Linux сообщение включает имя программы, имя файла с исходным кодом и номер строки, имя функции, а затем текст завершившегося неудачей условия. (В этом случае именованная константаassert()проявляется в виде своего макрорасширения 'NULL.)((void*)0)'Сообщение '
' означает, чтоAborted (core dumped)создала файлch12-assert; т.е. снимок адресного пространства процесса непосредственно перед его завершением. [122] Этот файл может быть использован впоследствии с отладчиком; см. раздел 15.3 «Основы GDB». Создание файлаcoreявляется намеренным побочным результатомcore; предполагается, что произошла решительная ошибка, и вы хотите исследовать процесс с помощью отладчика для ее определения.assert()Вы можете отменить оператор проверки, компилируя свою программу с помощью опции командной строки '
'. Когда этот макрос определен до включения-DNDEBUG, макрос<assert.h>расширяется в код, который ничего не делает. Например:assert()$ <b>gcc -DNDEBUG=1 ch12-assert.c -о ch12-assert</b> /* Компиляция с -DNDEBUG */$ <b>ch12-assert</b> /* Запуск */Segmentation fault (core dumped) /* Что случилось? */Здесь мы получили настоящий дамп ядра! Мы знаем, что операторы проверки были запрещены; сообщения «failed assertion» нет. Что же случилось? Рассмотрите строку 15
при вызове из строки 37lsearch(). В этом случае переменнаяmain()равнаarray. Доступ к памяти через указательNULLявляется ошибкой. (Технически различные стандарты оставляют «неопределенным» то, что происходит при разыменовывании указателяNULL. Наиболее современные системы делают то же, что и GNU/Linux; они завершают процесс, посылая ему сигналNULL; это, в свою очередь, создает дамп ядра. Этот процесс описан в главе 10 «Сигналы».SIGSEGV