Обработка ошибок в коде C

138
задан ubershmekel 6 November 2013 в 19:09
поделиться

11 ответов

Мне нравится ошибка как возвращаемое значение путь. Если Вы разрабатываете API, и Вы хотите использовать свою библиотеку, максимально безболезненную, думают об этих дополнениях:

  • хранилище все возможные состояния ошибки в одном typedef'ed перечислении и использовании это в Вашем lib. Только возвратите ints или еще хуже, смешайте ints или различные перечисления с кодами возврата.

  • обеспечивают функцию, которая преобразовывает ошибки во что-то человекочитаемое. Может быть простым. Просто ошибочное перечисление в, символ константы*.

  • я знаю, что эта идея делает многопоточное использование немного трудным, но было бы хорошо, если прикладной программист может установить глобальный ошибочный обратный вызов. Тем путем они смогут поместить точку останова в обратный вызов во время сессий поиска ошибки.

Hope это помогает.

66
ответ дан Nils Pipenbrinck 6 November 2013 в 19:09
поделиться

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

1
ответ дан dmityugov 6 November 2013 в 19:09
поделиться

EDIT:If Вам нужен доступ только к последней ошибке, и Вы не работаете в многопоточной среде.

можно возвратиться только истинный/ложный (или некоторый #define, если Вы работаете в C и не поддерживаете bool переменные), и имейте глобальный Ошибочный буфер, который будет содержать последнюю ошибку:

int getObjectSize(MYAPIHandle h, int* returnedSize);
MYAPI_ERROR LastError;
MYAPI_ERROR* getLastError() {return LastError;};
#define FUNC_SUCCESS 1
#define FUNC_FAIL 0

if(getObjectSize(h, &size) != FUNC_SUCCESS ) {
    MYAPI_ERROR* error = getLastError();
    // error handling
}
1
ответ дан Igor Oks 6 November 2013 в 19:09
поделиться

В дополнение к тому, что было сказано до возврата Вашего кода ошибки, исчерпывают утверждение или подобную диагностику, когда ошибка возвращается, поскольку это сделает трассировку намного легче. Путем я делаю это должно иметь специализированное, утверждают, что все еще компилируется в при выпуске, но только запущен, когда программное обеспечение находится в режиме диагностики с опцией тихо сообщить файлу журнала или паузе на экране.

я лично возвращаю коды ошибок как отрицательные целые числа с no_error как нуль, но он действительно оставляет Вас с возможной следующей ошибкой

if (MyFunc())
 DoSomething();

, альтернатива, возвратили отказ всегда как нуль и используют LastError () функция для предоставления подробной информации фактической ошибки.

2
ответ дан SmacL 6 November 2013 в 19:09
поделиться

Первый подход лучше, по моему скромному мнению:

  • легче записать функции тот путь. Когда Вы замечаете ошибку посреди функции, Вы просто возвращаете ошибочное значение. Во втором подходе необходимо ли присвоить ошибочное значение одному из параметров и затем возвратить ли что-то...., но что Вы возвратили бы - у Вас нет правильного значения, и Вы не возвращаете ошибочное значение.
  • это более популярно, таким образом, это будет легче понять, поддержит
4
ответ дан Klesk 6 November 2013 в 19:09
поделиться

Я сделал много C, программирующих в прошлом. И я действительно ценил возвращаемое значение кода ошибки. Но, имеет несколько возможных ловушек:

  • Дублирующиеся коды ошибки, это может быть решено с глобальным errors.h файлом.
  • Упущение проверить код ошибки, это должно быть решено с cluebat и долго отладкой часов. Но в конце Вы будете учиться (или Вы будете знать, что кто-то еще сделает отладку).
6
ответ дан Toon Krijthe 6 November 2013 в 19:09
поделиться

Я определенно предпочитаю первое решение:

int size;
if(getObjectSize(h, &size) != MYAPI_SUCCESS) {
  // Error handling
}

я немного изменил бы его, к:

int size;
MYAPIError rc;

rc = getObjectSize(h, &size)
if ( rc != MYAPI_SUCCESS) {
  // Error handling
}

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

И если бы мы уже говорящий об обработке ошибок я предложил бы goto Error; как код обработки ошибок, если приблизительно undo функция нельзя назвать для обработки обработки ошибок правильно.

3
ответ дан Ilya 6 November 2013 в 19:09
поделиться

Я лично предпочитаю бывший подход (возвращающий индикатор ошибки).

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

В Вашем getSize () пример я полагал бы, что размеры должны всегда быть нулем или положительный, настолько возвращающийся отрицательный результат может указать, что ошибка, во многом как системные вызовы UNIX делают.

я не могу думать ни о какой библиотеке, которой я пользовался, который идет для последнего подхода с ошибочным объектом, переданным в как указатель. stdio, и т.д. все идут с возвращаемым значением.

7
ответ дан Alnitak 6 November 2013 в 19:09
поделиться

Подход UNIX является самым подобным Вашему второму предложению. Возвратите или результат или сингл, "он пошел неправильно" значение. Например, открытый возвратит дескриптор файла на успехе или-1 при отказе. При отказе это также устанавливает errno, внешнее глобальное целое число для указания , который произошел отказ.

Если это имеет значение, Какао также принимало аналогичный подход. Много методов возвращают BOOL и берут NSError ** параметр, так, чтобы при отказе они установили ошибку и возвратились НЕТ. Затем обработка ошибок похожа:

NSError *error = nil;
if ([myThing doThingError: &error] == NO)
{
  // error handling
}

, который является где-нибудь между Вашими двумя опциями :-).

6
ответ дан 6 November 2013 в 19:09
поделиться

Я использовал оба подхода, и они оба хорошо работали для меня. Какой бы ни один я использую, я всегда пытаюсь применить этот принцип:

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

утверждение, которое проверяет исходные данные ясно, передает то, что ожидает функция, в то время как слишком много проверки ошибок может затенить логику программы. Решение, что сделать для всех различных ошибочных случаев, может действительно усложнить дизайн. Почему фигура, как functionX должен обработать нулевого указателя, если можно вместо этого настоять, чтобы программист никогда не передавал тот?

87
ответ дан AShelly 6 November 2013 в 19:09
поделиться

Я использую первый подход каждый раз, когда я создаю библиотеку. Существует несколько преимуществ использования typedef'ed перечисления как код возврата.

  • , Если функция возвращает более сложный вывод, такой как массив и это - длина, которую Вы не должны создавать произвольные структуры для возврата.

    rc = func(..., int **return_array, size_t *array_length);
    
  • Это допускает простую, стандартизированную обработку ошибок.

    if ((rc = func(...)) != API_SUCCESS) {
       /* Error Handling */
    }
    
  • Это допускает простую обработку ошибок в библиотечной функции.

    /* Check for valid arguments */
    if (NULL == return_array || NULL == array_length)
        return API_INVALID_ARGS;
    
  • Используя typedef'ed перечисление также позволяет, чтобы перечислимое имя было видимо в отладчике. Это допускает более легкую отладку без потребности постоянно консультироваться с заголовочным файлом. Наличие функции для перевода этого перечисления в строку полезно также.

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

17
ответ дан Jeffrey Cohen 6 November 2013 в 19:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: