Как использовать 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;
}