стратегии обработки ошибок в C? - PullRequest
5 голосов
/ 03 мая 2010

Учитывая код ниже:

typedef struct {int a;} test_t;

arbitrary_t test_dosomething(test_t* test) {
    if (test == NULL) {
        //options:
        //1. print an error and let it crash
          //e.g. fprintf(stderr, "null ref at %s:%u", __FILE__, __LINE__);
        //2. stop the world
          //e.g. exit(1);
        //3. return (i.e. function does nothing)
        //4. attempt to re-init test
    }
    printf("%d", test->a); //do something w/ test
}

Я хочу получить ошибку компилятора, если test когда-либо NULL, но я полагаю, что это невозможно в C. Поскольку мне нужно выполнять проверку на ноль во время выполнения, какой вариант является наиболее подходящим способом ее обработки?

Ответы [ 8 ]

7 голосов
/ 03 мая 2010

Если вы никогда не хотите, чтобы функция вызывалась с нулевым указателем (т.е. указатель, не являющийся нулевым, является предварительным условием для вызова функции), вы можете использовать утверждение в верхней части функции:

assert(test != NULL);

Это может помочь вам при отладке найти места в вашем коде, где вы вызываете функцию с нулевым указателем.

4 голосов
/ 03 мая 2010

Мы действительно не можем категорически ответить на этот вопрос. Это сильно зависит от того, для чего будет использоваться программное обеспечение. Если он обеспечивает излучение, вы бы хотели, чтобы он напечатал ошибку и exit(1);. Если вы пишете бизнес-приложение, вы, вероятно, захотите зарегистрировать ошибку и просто вернуться. Все дело в контексте.

1 голос
/ 19 апреля 2011

Использовать setjmp .

http://en.wikipedia.org/wiki/Setjmp.h

http://aszt.inf.elte.hu/~gsd/halado_cpp/ch02s03.html

#include <setjmp.h>
#include <stdio.h>

jmp_buf x;

void f()
{
    longjmp(x,5); // throw 5;
}

int main()
{
    // output of this program is 5.

    int i = 0;

    if ( (i = setjmp(x)) == 0 )// try{
    {
        f();
    } // } --> end of try{
    else // catch(i){
    {
        switch( i )
        {
        case  1:
        case  2:
        default: fprintf( stdout, "error code = %d\n", i); break;
        }
    } // } --> end of catch(i){
    return 0;
}
1 голос
/ 03 мая 2010

Я бы использовал assert в начале функции:

arbitrary_t test_dosomething(test_t* test) {
    assert(test != NULL);
    printf("%d", test->a); //do something w/ test
}

Вы не получите ошибку компилятора. Но в процессе разработки (отладки) ваша программа остановится и укажет вам на конкретный код assert.

0 голосов
/ 03 мая 2010

Я могу только повторить вышесказанное - это зависит от вашего приложения.

Хорошее практическое правило - выявлять ошибки как можно раньше. Как правило, это помогает легче, быстрее и дешевле найти и исправить проблему. В идеале вы бы поймали это во время разработки. Как вы указали, вы не можете поймать его во время компиляции (если не переключитесь на язык «дизайн по контракту», такой как Eiffel), поэтому вы поймаете его во время выполнения.

А потом ... это зависит ...

Если это настольное приложение, то, возможно, огромное диалоговое окно и выход - самый быстрый способ исправить его, если он заставляет кого-то сообщать об ошибке. Если вы можете, это также может быть идея отправить электронное письмо разработчику (например, если это собственное приложение).

Если это критическое или жизненно важное значение, возможно, вам просто нужно перезапустить приложение (это общий подход во встроенных системах).

Что бы вы ни решили, постарайтесь собрать как можно больше информации о проблеме. Например, вы можете перевернуть свой выигранный макрос, чтобы обернуть вокруг ASSERT, который добавляет FILE и LINE ).

Я использую следующее:

#ifdef TESTING
#define ASSERT_MSG(subsystem, message, condition) if (!(condition)) {printf("Assert failed: \"%s\" at line %d in file \"%s\"\n", message, __LINE__, __FILE__); fflush(stdout); abort();}

/* we can also use this, which prints of the failed condition as its message */
#define ASSERT_CONDITION(subsystem, condition) if (!(condition)) {printf("Assert failed: \%s\" at line %d in file \%s\"\n", #condition, __LINE__, __FILE__); fflush(stdout); abort();}
#else
#define ASSERT_MSG(subsystem, message, condition)  if (!condition) DebugTrace(FATAL, subsystem, __FILE__, __LINE__, "%s", message);
#define ASSERT_CONDITION(subsystem, condition)     if (!(condition)) DebugTrace(FATAL, subsystem, __FILE__, __LINE__, "%s", #condition);
#endif
0 голосов
/ 03 мая 2010

Очевидно (я недавно искал в Интернете "C static assert"), C ++ 0x имеет static_assert, и люди C говорят о чем-то подобном и для следующего стандарта C. Конечно, это подразумевает, что в настоящее время нет стандартного «статического утверждения» в C.

Некоторые люди предлагают макросы препроцессора C, которые определяют перечисления или структуры, которые используют логическое выражение и оператор ?: для генерации недопустимого определения enum / struct в случае сбоя условного выражения. (Над веб-поиском перечислено несколько таких макроопределений.)

Лично, так как я всегда использую gcc, я просто объявляю такие функции как

foo_t function(moo_t *moo) __attribute__(( nonnull(1) ));

и, таким образом, компилятор выдаст мне предупреждение (-Wnonnull, включенное в -Wall), если я случайно вызову функцию с параметром, установленным в NULL.

0 голосов
/ 03 мая 2010

Вы не можете получить ошибку компилятора, если test равен NULL, это значение времени выполнения, и оно может зависеть даже от пользовательского ввода, компилятор не может узнать его значение.

Чтобы избежать вызова функции со значением NULL, используйте assert:

assert(test != NULL);

Это означает, что мой код не может обрабатывать NULL как ввод. И он будет прерван во время выполнения.

Ваша функция должна возвращать произвольный_т, и она ничего не возвращает.

Вы также можете отправить ошибку обратно вызывающей стороне, в возвращаемом значении, которое вы не используете сейчас, например, возвращая вместо этого произвольный_t *, который будет НЕДЕЙСТВИТЕЛЕН, если он не может быть распределен (что будет иметь место если test_t был NUL). Вызывающий должен проверить это.

0 голосов
/ 03 мая 2010

Это действительно зависит от вашего варианта использования. Если у вас есть интерактивное приложение, предложите пользователю сделать правильный ввод. Если это процесс сервера, вы можете войти. Если из-за ошибки ваша программа пойдет на юг, сделайте assert.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...