Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Название:Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:0101
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015 краткое содержание
Язык программирования C. Лекции и упражнения (6-е изд.) 2015 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
#include
int comp(const void * p1, const void * p2) /* обязательная форма */
{
/* получение правильного типа указателя */ const struct names *psl = (const struct names *) p1; const struct names *ps2 = (const struct names *) p2; int res;
res = strcmp(psl->last, ps2->last); /* сравнение фамилий */
if (res |= 0) return res;
else /* фамилии одинаковы, поэтому сравнить имена */
return strcmp(psl->first, ps2->first);
)
В данной функции сравнение осуществляется с помощью функции strcmp(), которая возвращает значения, удовлетворяющие требованиям к функции сравнения. Обратите внимание, что для применения операции -> необходим указатель на структуру.
Библиотека утверждений
Библиотека утверждений, поддерживаемая заголовочным файлом assert.h — это небольшая библиотека, предназначенная для оказания содействия при отладке программы. Она состоит из макроса по имени assert(). Макрос принимает в качестве аргумента целочисленное выражение. Если выражение оценивается как ложное (ненулевое), макрос assert() выводит в стандартный поток ошибок (stderr) сообщение об ошибке и вызывает функцию abort(), которая прекращает выполнение программы. (Прототип функции abort() находится в заголовочном файле stdlib.h.) Идея состоит в том, чтобы идентифицировать критические места в программе, где должны быть истинными определенные условия, и с помощью оператора assert() завершать программу, если одно из указанных условий нарушается. Обычно аргументом служит выражение отношения или логическое выражение. Когда assert() прекращает выполнение программы, сначала отображается не прошедший проверку тест, имя файла, содержащего этот тест, и номер строки, где находится тест.
Использование assert()
В листинге 16.18 приведен простой пример применения assert(). В нем утверждается, что значение z должно быть больше или равно 0, прежде чем будет предпринята попытка извлечь из него квадратный корень. Кроме того, ошибочно выполняется вычитание значения вместо его сложения, делая возможным получение переменной z недопустимого значения.
Препроцессор и библиотека С 705
Листинг 16.18. Программа assert.с
Ниже показаны результаты пробного запуска:
Введите пару чисел (0 0 для завершения) :
4 3
результатом является 2.645751
Введите следующую пару чисел:
5 3
результатом является 4.000000
Введите следующую пару чисел:
3 5
Assertion failed: (z >= 0), function main, file /Users/assert.c, line 14.
Отказ утверждения: (z >= 0), функция main, файл /Users/assert. с, строка 14.
Точный текст в последней строке зависит от компилятора. Потенциально может сбивать с толку то, что в сообщении не говорится об условии z >= 0; вместо этого в нем уведомляется о том, что отказало утверждение z >= 0.
Чего-то похожего можно было бы добиться с помощью оператора if:
if (z < 0)
{
puts("z меньше 0"); abort();
}
Тем не менее, подход с assert() обладает рядом преимуществ. Он автоматически идентифицирует файл и номер строки, где возникла проблема. Наконец, существует механизм включения и отключения макроса assert() без необходимости в изменении кода. Если вы считаете, что устранили ошибки в программе, поместите следующее определение макроса
#define NDEBUG
перед местом включения файла assert .h, повторно скомпилируйте программу, и компилятор отключит в файле все операторы assert(). Если проблема возникнет снова, можете удалить директиву #define (или закомментировать ее) и провести повторную компиляцию, в результате чего все операторы assert() снова активизируются.
706 глава 16
_Static_assert (С11)
Выражение assert() проверяется во время выполнения. В С11 появилось новое средство в форме объявления _Static_assert, которое осуществляет проверку на этапе компиляции. Таким образом, assert() может привести к прерыванию выполняющейся программы, тогда как _Static_assert() может стать причиной того, что программа не компилируется. Объявление _Static_assert принимает два аргумента. Первым из них является целочисленное константное выражение, а вторым — строка. Если выражение оценивается в 0 (или _False), то компилятор отобразит строку, и не будет компилировать программу. Давайте рассмотрим короткий пример в листинге 16.19, после чего взглянем на отличия между assert() и _Static_assert().
Листинг 16.19. Программа statasrt.c
Ниже показана попытка проведения компиляции в командной строке:
$ clang statasrt.c
statasrt.с:4:1: error: static_assert failed "Ошибочно предполагается 16битовый тип char"
_Static_assert(CHAR_BIT == 16, "Ошибочно предполагается 16-битовый тип char");
1 error generated.
$
statasrt.с;4;1; ошибка: отказ static_assert "Ошибочно предполагается 16битовый тип char"
_Static_assert (CHAR_BIT == 16, "Ошибочно предполагается 16-битовый тип char");
1 ошибка сгенерирована.
$
Синтаксически _Static_assert трактуется как оператор объявления. Следовательно, в отличие от большинства разновидностей операторов С, он может находиться либо в функции, либо (как в данном случае) быть внешним по отношению к функции.
Требование о том, что первым аргументом в _Static_assert должно быть целочисленное константное выражение, гарантирует возможность его оценки на этапе компиляции. (Вспомните, что выражения sizeof считаются целочисленными константами.) Поэтому в листинге 16.18 вы не можете подставить _Static_assert вместо assert(), т.к. для проверочного выражения в программе используется z > 0, которое является неконстантным и может быть вычислено только во время выполнения. В листинге 16.19 можно было бы применить assert (CHAR_BIT ==16) в теле main(), но это привело бы к выдаче предупреждения об ошибке лишь после компиляции и запуска программы, что менее эффективно.
Препроцессор и библиотека С 707
В заголовочном файле assert.h идентификатор static_assert определен как псевдоним для ключевого слова _Static_assert. Это делает С более совместимым с языком C++, в котором для рассмотренной возможности static assert используется в качестве ключевого слова.
Функции memcpy() И memmove()
ИЗ библиотеки string.h
Присваивать один массив другому нельзя, поэтому в таких случаях мы применяли циклы для поэлементного копирования одного массива в другой. Единственное исключение состоит в том, что для символьных массивов мы использовали функции strcpy() и strncpy(). Функции memcpy() и memmove() предлагают почти такие же услуги для других видов массивов. Рассмотрим прототипы этих функций:
void *memcpy(void * restrict si, const void * restrict s2, size_t n);
void *memmove (void *sl, const void *s2, size_t n);
Обе функции копируют n байтов из области, на которую указывает аргумент s2, в область, указанную аргументом si, и обе они возвращают значение si. Различие между этими двумя функциями, как указывает ключевое слово restrict, связано с тем, что memcpy() разрешено полагать, что две области памяти нигде не перекрываются друг с другом. Функция memmove() не делает такого предположения, поэтому копирование происходит так, как будто все байты сначала помещаются во временный буфер и только затем копируются в область назначения. Что произойдет, если применить memcpy() к перекрывающимся областям? В этом случае поведение функции не определено, т.е. она может как работать, так и не работать. Компилятор не запрещает использование функции memcpy(), когда этого делать не следует, поэтому именно вы несете ответственность за обеспечение того, что области памяти не перекрываются. Это еще одна часть тяжкой нощи программиста.
Читать дальшеИнтервал:
Закладка: