Как использовать C assert, чтобы сделать код более безопасным?
Чтение разное. уроки, связанные с разработкой SDL. Я нашел два разных примера, делая то же самое, но по-другому. Мне было интересно, какой из этих двух вы считаете правильным, судя с точки зрения "безопасности" кода и ремонтопригодности.
В первом примере программист вообще не использует assert, но код выглядит нормально (по крайней мере для моего глаза):
int main(){
SDL_Surface *screen;
/** Initialize SDL */
if(SDL_Init(SDL_INIT_VIDEO)!=0){
fprintf(stderr,"Unable to initialize SDL: %s",SDL_GetError());
}
atexit(SDL_Quit);
/** Sets video mode */
screen=SDL_SetVideoMode(640,480,16,SDL_HWSURFACE);
if(screen==NULL){
fprintf(stderr,"Unable to set video mode: %s",SDL_GetError());
}
return (0);
}
Во втором примере программист [другой] использует другой подход, что-то вроде (код не совсем копирует-вставляет):
int main(){
SDL_Surface* screen;
/** Initialize SDL */
assert(SDL_Init(SDL_INIT_VIDEO)==0);
atexit(SDL_Quit);
/** Sets video mode */
screen=SDL_SetVideoMode(640,480,16,SDL_HWSURFACE);
assert(screen!=NULL);
return (0);
}
Можно ли "подставить" в условиях if (из первого примера) с утверждениями, как во втором примере?
Какая правильная стратегия (если есть)?
Ответы
Ответ 1
Это не нормально делать эту замену. Второй пример неверен, потому что assert(x)
не имеет ничего общего с не-отладочными строками (когда NDEBUG
). Это означает, что указатель, проверенный в assert
выше, удаляется из кода в версиях сборки. Это определенно неправильно.
Итак, когда следует использовать assert
? Это полезно для документирования и отладки. В некотором смысле вы говорите: "Я уверен, что это условие истинно, и помещаю его здесь как assert
, чтобы поймать плохой код во время отладки и документировать условие для читателей кода".
Итак, между двумя примерами существует большая разница. Для таких вещей, как проверка возвращаемого значения malloc
, assert
неверно, потому что нет гарантии, что они вернут не NULL
, и, как я уже упоминал выше, assert(x)
означает "Я полностью уверен, что x
is true", а не только "If x
не верно, это плохо". Для этого используется элемент управления if(x) good(); else bad();
.
SDL_Init
и SDL_SetVideoMode
могут возвращать -1
и NULL
соответственно.
Ответ 2
assert
следует использовать, когда что-то идет не так, неожиданным образом. Обычно, если утверждение терпит неудачу, это означает, что в программе есть ошибка. Утверждения не используются для ожидаемых ошибок, которые могут произойти (т.е. Не удалось открыть файл, не удалось инициализировать что-либо и т.д.).
В представленном вами примере assert
не похоже на наиболее логичное решение. Когда программе не удалось инициализировать SDL, имеет смысл рассказать об этом пользователю структурированным способом, чем бросить утверждение (что может привести к сбою seg в некоторых системах).
Ответ 3
Операторы утверждения обычно используются только для отладочных сборников и будут останавливать программу и часто позволяют вам входить в отладчик. Вероятно, в релиз-сборке имеет смысл проверять наличие ошибок. Один простой способ может быть таким:
assert( condition );
if ( !condition )
handle error;
Ответ 4
Я использую утверждение для документирования предварительных условий и постусловий.
(определение намерения функции)
как..
double positive_division(double dividend, double divisor)
{
//preconditions
ASSERT(dividend>=0);
ASSERT(divisor >0);
double quotient = dividend/divisor;
//postconditions
ASSERT(quotient>=0);
return quotient;
}