C Language
Утверждение
Поиск…
Вступление
Утверждение является предикатом, что представленное условие должно быть истинным в момент, когда утверждение встречается программным обеспечением. Наиболее распространенными являются простые утверждения , которые проверяются во время выполнения. Тем не менее, статические утверждения проверяются во время компиляции.
Синтаксис
- утверждает (выражение)
- static_assert (выражение, сообщение)
- _Static_assert (выражение, сообщение)
параметры
параметр | подробности |
---|---|
выражение | выражение скалярного типа. |
сообщение | строковый литерал, который будет включен в диагностическое сообщение. |
замечания
Оба assert
и static_assert
- это макросы, определенные в assert.h
.
Определение assert
зависит от макроса NDEBUG
который не определен стандартной библиотекой. Если NDEBUG
определен, assert
- это no-op:
#ifdef NDEBUG # define assert(condition) ((void) 0) #else # define assert(condition) /* implementation defined */ #endif
Мнение зависит от того, следует ли использовать NDEBUG
для компиляции продукции.
- Про-лагерей утверждает, что сообщения
assert
abort
и assertion не полезны для конечных пользователей, поэтому результат не помогает пользователю. Если у вас есть фатальные условия для проверки производственного кода, вы должны использовать обычные условияif/else
иexit
илиquick_exit
для завершения программы. В отличие отabort
, они позволяют программе выполнять некоторую очистку (через функции, зарегистрированные с помощьюatexit
илиat_quick_exit
). - Кон-лагерь утверждает, что вызовы
assert
никогда не должны запускаться в производственном коде, но если они это сделают, проверенное условие означает, что что-то резко ошибочно, и программа будет плохо себя вести, если исполнение продолжается. Поэтому лучше иметь утверждения, действующие в производственном кодексе, потому что, если они стреляют, ад уже сломался. - Другой вариант - использовать систему утверждений, которая всегда выполняет проверку, но обрабатывает ошибки по-разному между разработкой (когда это
abort
подходит) и производством (где «неожиданная внутренняя ошибка - обратитесь в службу технической поддержки» может быть более уместным).
static_assert
расширяется до _Static_assert
который является ключевым словом. Условие проверяется во время компиляции, поэтому condition
должно быть постоянным выражением. Нет необходимости в том, чтобы это было по-разному обрабатываться между развитием и производством.
Предварительное условие и постусловие
Одним из предпосылок для утверждения является предварительное условие и постусловие. Это может быть очень полезно для поддержания инварианта и дизайна по контракту . Например, длина всегда равна нулю или положительна, поэтому эта функция должна возвращать нулевое или положительное значение.
#include <stdio.h>
/* Uncomment to disable `assert()` */
/* #define NDEBUG */
#include <assert.h>
int length2 (int *a, int count)
{
int i, result = 0;
/* Precondition: */
/* NULL is an invalid vector */
assert (a != NULL);
/* Number of dimensions can not be negative.*/
assert (count >= 0);
/* Calculation */
for (i = 0; i < count; ++i)
{
result = result + (a[i] * a[i]);
}
/* Postcondition: */
/* Resulting length can not be negative. */
assert (result >= 0);
return result;
}
#define COUNT 3
int main (void)
{
int a[COUNT] = {1, 2, 3};
int *b = NULL;
int r;
r = length2 (a, COUNT);
printf ("r = %i\n", r);
r = length2 (b, COUNT);
printf ("r = %i\n", r);
return 0;
}
Простое утверждение
Утверждение - это утверждение, используемое для утверждения, что факт должен быть правдой, когда эта строка кода будет достигнута. Утверждения полезны для обеспечения выполнения ожидаемых условий. Когда условие, переданное утверждению, истинно, действия нет. Поведение в ложных условиях зависит от флагов компилятора. Когда утверждения разрешены, ложный ввод вызывает немедленную остановку программы. Когда они отключены, никаких действий не предпринимается. Общепринятой практикой является включение утверждений во внутренние и отладочные сборки и их отключение в сборках релизов, хотя утверждения часто включаются в выпуске. (Оправдывание прекращается или хуже, чем ошибки зависят от программы.) Утверждения должны использоваться только для того, чтобы ловить внутренние ошибки программирования, что обычно означает, что передаются плохие параметры.
#include <stdio.h>
/* Uncomment to disable `assert()` */
/* #define NDEBUG */
#include <assert.h>
int main(void)
{
int x = -1;
assert(x >= 0);
printf("x = %d\n", x);
return 0;
}
Возможный выход с неопределенным NDEBUG
:
a.out: main.c:9: main: Assertion `x >= 0' failed.
Возможный вывод с помощью NDEBUG
:
x = -1
Хорошая практика - определить NDEBUG
глобально, чтобы вы могли легко скомпилировать свой код со всеми утверждениями как NDEBUG
, так и выключить. Легкий способ сделать это - определить NDEBUG
в качестве опции для компилятора или определить его в общем заголовке конфигурации (например, config.h
).
Статическое утверждение
Статические утверждения используются для проверки правильности условия при компиляции кода. Если это не так, компилятор должен выпустить сообщение об ошибке и остановить процесс компиляции.
Статическое утверждение - это такое, которое проверяется во время компиляции, а не время выполнения. Условие должно быть константным выражением, и если false приведет к ошибке компилятора. Первый аргумент, проверяемое условие, должен быть константным выражением, а второй - строковым литералом.
В отличие от assert, _Static_assert
- это ключевое слово. Удобный макрос static_assert
определяется в <assert.h>
.
#include <assert.h>
enum {N = 5};
_Static_assert(N == 5, "N does not equal 5");
static_assert(N > 10, "N is not greater than 10"); /* compiler error */
До C11 не было прямой поддержки статических утверждений. Однако в C99 статические утверждения могут быть эмулированы с помощью макросов, которые могут вызвать сбой компиляции, если условие времени компиляции было ложным. В отличие от _Static_assert
, второй параметр должен быть правильным именем маркера, чтобы с ним можно было создать имя переменной. Если утверждение не выполняется, имя переменной отображается в ошибке компилятора, так как эта переменная использовалась в синтаксически неправильном объявлении массива.
#define STATIC_MSG(msg, l) STATIC_MSG2(msg, l)
#define STATIC_MSG2(msg,l) on_line_##l##__##msg
#define STATIC_ASSERT(x, msg) extern char STATIC_MSG(msg, __LINE__) [(x)?1:-1]
enum { N = 5 };
STATIC_ASSERT(N == 5, N_must_equal_5);
STATIC_ASSERT(N > 5, N_must_be_greater_than_5); /* compile error */
До C99 вы не могли объявлять переменные в произвольных местоположениях в блоке, поэтому вам пришлось бы очень осторожно относиться к использованию этого макроса, гарантируя, что он появляется только там, где объявление переменной будет действительным.
Утверждение недостижимого кода
Во время разработки, когда определенные пути кода должны быть недоступны для потока управления, вы можете использовать assert(0)
чтобы указать, что такое условие является ошибочным:
switch (color) {
case COLOR_RED:
case COLOR_GREEN:
case COLOR_BLUE:
break;
default:
assert(0);
}
Всякий раз, когда аргумент макроса assert()
вычисляет false, макрос будет записывать диагностическую информацию в стандартный поток ошибок, а затем прервать программу. Эта информация включает в себя номер файла и строки оператора assert()
и может быть очень полезной при отладке. Зарезервировать можно, NDEBUG
макрос NDEBUG
.
Другой способ прервать программу при возникновении ошибки являются стандартной библиотеки функций exit
, quick_exit
или abort
. exit
и quick_exit
принимают аргумент, который можно передать обратно в вашу среду. abort()
(и, таким образом, assert
) может быть действительно серьезным завершением вашей программы, и некоторые очистки, которые в противном случае были бы выполнены в конце выполнения, могут не выполняться.
Основным преимуществом assert()
является то, что он автоматически печатает отладочную информацию. Вызов abort()
имеет то преимущество, что он не может быть отключен как assert, но может не вызывать отображение какой-либо информации отладки. В некоторых ситуациях использование обеих конструкций вместе может быть полезным:
if (color == COLOR_RED || color == COLOR_GREEN) {
...
} else if (color == COLOR_BLUE) {
...
} else {
assert(0), abort();
}
Когда assert()
включены , вызов assert()
будет печатать отладочную информацию и завершать программу. Выполнение никогда не достигает вызова abort()
. Когда assert()
отключены , вызов assert()
ничего не делает и вызывается abort()
. Это гарантирует, что программа всегда заканчивается для этого условия ошибки; включение и отключение утверждений влияет только на то, напечатан ли вывод отладки.
Вы никогда не должны оставлять такое assert
в производственном коде, потому что отладочная информация не полезна для конечных пользователей, и поскольку abort
обычно является слишком строгим завершением, которое блокирует обработчики очистки, которые установлены для exit
или quick_exit
для запуска.
Сообщения об ошибках подтверждения
Существует трюк, который может отображать сообщение об ошибке вместе с утверждением. Обычно вы пишете такой код
void f(void *p)
{
assert(p != NULL);
/* more code */
}
Если утверждение не выполнено, сообщение об ошибке будет напоминать
Утверждение не выполнено: p! = NULL, файл main.c, строка 5
Однако вы можете использовать логическое И ( &&
), чтобы дать сообщение об ошибке
void f(void *p)
{
assert(p != NULL && "function f: p cannot be NULL");
/* more code */
}
Теперь, если утверждение не выполняется, сообщение об ошибке будет читать что-то вроде этого
Утверждение не выполнено: p! = NULL && "функция f: p не может быть NULL", файл main.c, строка 5
Причина в том, почему это работает, заключается в том, что строковый литерал всегда вычисляет ненулевое значение (true). Добавление && 1
к булевому выражению не влияет. Таким образом, добавление && "error message"
имеет никакого эффекта, за исключением того, что компилятор отобразит полное выражение, которое не удалось.