Как хороший программист, нужно писать надежные коды, которые будут обрабатывать каждый результат его программы. Однако почти все функции из библиотеки C будут возвращать 0, -1 или NULL в случае ошибки.
Иногда очевидно, что необходима проверка ошибок, например, когда вы пытаетесь открыть файл. Но я часто игнорирую проверку ошибок в функциях, таких как printf
или даже malloc
потому, что я не чувствую необходимости.
if(fprintf(stderr, "%s", errMsg) < 0){
perror("An error occurred while displaying the previous error.");
exit(1);
}
Это хорошая практика, чтобы просто игнорировать определенные ошибки, или есть лучший способ обработать все ошибки?
c
error-handling
Дерек 朕 會 功夫
источник
источник
try
оператором, поэтому вам не нужно проверять каждый отдельный вызов или операцию. (Также обратите внимание, что некоторые языки лучше других обнаруживают простые ошибки, такие как нулевая разыменование или индекс массива за пределами границ.)errno
! В случае, если вы не знакомы, хотя это правда, что «почти все функции из библиотеки C будут возвращать 0 или -1 илиNULL
в случае ошибки», они также устанавливают глобальнуюerrno
переменную , к которой вы можете получить доступ, используя,#include <errno.h>
а затем просто читая стоимостьerrno
. Так, например, еслиopen
возвращается (2)-1
, вы можете проверитьerrno == EACCES
, будет ли это указывать на ошибку прав доступа или на тоENOENT
, что запрошенный файл не существует.try
/catch
, хотя вы можете смоделировать его с помощью прыжков.Ответы:
В общем, код должен иметь дело с исключительными условиями, где это уместно. Да, это расплывчатое утверждение.
В языках более высокого уровня с программной обработкой исключений это часто обозначается как «поймать исключение в методе, где вы действительно можете что-то с этим сделать». Если произошла ошибка файла, возможно, вы позволили ему скопировать стек в коде пользовательского интерфейса, который может фактически сказать пользователю «ваш файл не удалось сохранить на диск». Механизм исключений эффективно поглощает «каждую маленькую ошибку» и неявно обрабатывает ее в соответствующем месте.
В С у вас нет такой роскоши. Существует несколько способов обработки ошибок, некоторые из которых являются функциями языка / библиотеки, а некоторые - методами кодирования.
Игнорировать определенные ошибки? Может быть. Например, разумно предположить, что запись в стандартный вывод не завершится неудачей. В случае неудачи, как бы вы сказали пользователю? Да, это хорошая идея - игнорировать определенные ошибки или защищаться от них, чтобы предотвратить их. Например, проверьте на ноль перед делением.
Есть способы обработать все или, по крайней мере, большинство ошибок:
Каскадные
if
с:Проверьте первое условие, например, открытие или создание файла, а затем предположите, что последующие операции выполнены успешно.
Надежный код хорош, и нужно проверять и обрабатывать ошибки. Какой метод лучше всего подходит для вашего кода, зависит от того, что делает код, насколько критичен сбой и т. Д., И только вы действительно можете ответить на него. Тем не менее, эти методы проверены в бою и используются в различных проектах с открытым исходным кодом, где вы можете посмотреть, как реальный код проверяет наличие ошибок.
источник
stdout
может быть перенаправлен в файл или вообще не существовать в зависимости от системы.stdout
это не поток «запись в консоль», это обычно так , но это не обязательно.stdout
... очевидного :-)dump_database > backups/db_dump.txt
не удается записать в стандартный вывод в любой заданной точке, я бы не хотел, чтобы она продолжалась и успешно завершалась. (Не то, чтобы базы данных создавались таким образом, но точка зрения остается неизменной)Да, но вы знаете, какую функцию вы вызывали ?
На самом деле у вас много информации, которую вы можете поместить в сообщение об ошибке. Вы знаете, какая функция была вызвана, имя функции, которая ее вызывала, какие параметры были переданы и возвращаемое значение функции. Это достаточно информации для очень информативного сообщения об ошибке.
Вам не нужно делать это для каждого вызова функции. Но в первый раз, когда вы видите сообщение об ошибке «Произошла ошибка при отображении предыдущей ошибки», когда вам действительно нужна была полезная информация, это будет последний раз, когда вы увидите это сообщение об ошибке, потому что вы немедленно собираетесь изменить сообщение об ошибке на что-то информативное, что поможет вам устранить проблему.
источник
if (!myfunc()) {/*proceed*/}
. 0 не было ошибкой, а все ненулевые были своего рода ошибкой, и у каждой ошибки был свой ненулевой код. как сказал мой друг, «есть только одна Истина, но много лжи». «0» должно быть «true» вif()
тесте, а все, что не равно нулю, должно быть «false».if(function()) { // do something }
читается как «если функция выполняется без ошибок, то что-то делать». или, что еще лучше, «если функция выполняется успешно, сделайте что-нибудь». Серьезно, тех, кто понимает соглашение, это не смущает. Возвратfalse
(или 0) при возникновении ошибки не позволит использовать коды ошибок. Ноль означает ложь в C, потому что так работает математика. См. Programmers.stackexchange.com/q/198284TLDR; Вы почти никогда не должны игнорировать ошибки.
В языке C отсутствует хорошая функция обработки ошибок, поэтому каждый разработчик библиотеки может реализовать свои собственные решения. Более современные языки имеют встроенные исключения, которые значительно облегчают решение этой конкретной проблемы.
Но когда вы застряли с C, у вас нет таких льгот. К сожалению, вам просто придется платить цену каждый раз, когда вы вызываете функцию, которая может привести к сбою. Или же вы будете страдать от гораздо худших последствий, таких как непреднамеренная перезапись данных в памяти. Поэтому, как правило, вы всегда должны проверять наличие ошибок.
Если вы не проверяете возвращение,
fprintf
вы, скорее всего, оставляете ошибку, которая в лучшем случае не будет делать то, что ожидает пользователь, а в худшем случае взорвет всю вещь во время полета. Там нет оправдания, чтобы подорвать себя таким образом.Однако, как разработчик C, ваша задача - сделать код легким в обслуживании. Поэтому иногда вы можете спокойно игнорировать ошибки для ясности, если (и только если) они не представляют какой-либо угрозы для общего поведения приложения. ,
Это та же проблема, что и при этом:
Если вы видите, что без пустых комментариев внутри пустого блока catch это, безусловно, проблема. Нет никаких оснований полагать, что вызов
malloc()
будет выполнен успешно в 100% случаев.источник
malloc()
это одна функция, которую вы определенно хотите проверить возвращаемые значения!Вопрос на самом деле не зависит от языка, а скорее от пользователя. Подумайте над вопросом с точки зрения пользователя. Пользователь делает что-то, например, вводит имя программы в командной строке и нажимает ввод. Что ожидает пользователь? Как они могут определить, что что-то пошло не так? Могут ли они позволить себе вмешаться в случае ошибки?
Во многих видах кода такие проверки излишни. Однако в высоконадежном коде безопасности, таком как коды для ядерных реакторов, проверка патологических ошибок и запланированные пути восстановления являются частью повседневного характера работы. Считается, что стоит потратить время на то, чтобы спросить: «Что произойдет, если Х выйдет из строя? Как мне вернуться в безопасное состояние?» В менее надежном коде, таком как код для видеоигр, вы можете избежать гораздо меньшего количества ошибок.
Другой похожий подход - насколько вы можете улучшить состояние, улавливая ошибку? Я не могу сосчитать количество программ на C ++, которые гордо отлавливают исключения, только для того, чтобы просто выбросить их, потому что они на самом деле не знали, что с ними делать ... но они знали, что должны обрабатывать исключения. Такие программы ничего не получили от лишних усилий. Добавляйте только код проверки ошибок, который, по вашему мнению, может справиться с ситуацией лучше, чем просто не проверять код ошибки. Тем не менее, в C ++ есть особые правила для обработки исключений, возникающих во время обработки исключений, для того, чтобы перехватить их более осмысленным образом (и под этим я имею в виду вызов terminate (), чтобы убедиться, что погребальный костер, который вы создали для себя, загорелся в своей славе)
Насколько патологическим вы можете получить? Я работал над программой, которая определила «SafetyBool», чьи истинные и ложные значения были тщательно выбраны, чтобы иметь равномерное распределение единиц и нулей, и они были выбраны так, чтобы любой из множества аппаратных сбоев (однобитовые перевороты, шина данных следы поломки и т. д.) не приводили к неверному истолкованию логического значения. Излишне говорить, что я бы не стал утверждать, что это практика программирования общего назначения, используемая в любой старой программе.
источник
источник
Немного абстрактного взгляда на вопрос. И это не обязательно для языка Си.
Для больших программ у вас будет слой абстракции; возможно, часть двигателя, библиотеки или в рамках. Этот слой не будет заботиться о погоде вы получите достоверные данные или результат будет значение по умолчанию некоторое:
0
,-1
, иnull
т.д.Тогда есть слой, который будет вашим интерфейсом к абстрактному слою, который будет выполнять большую часть обработки ошибок и, возможно, других вещей, таких как внедрение зависимостей, прослушивание событий и т. Д.
И позже у вас будет конкретный уровень реализации, где вы на самом деле устанавливаете правила и обрабатываете результат.
Поэтому я считаю, что иногда лучше полностью исключить обработку ошибок из части кода, потому что эта часть просто не выполняет эту работу. А потом есть какой-нибудь процессор, который оценил бы вывод и укажет на ошибку.
В основном это делается для разделения обязанностей, что приводит к удобочитаемости кода и лучшей масштабируемости.
источник
В общем, если у вас нет веских причин не проверять это состояние ошибки, вы должны это сделать. Единственная веская причина, по которой я могу подумать, чтобы не проверять состояние ошибки, - это когда вы не можете сделать что-то значимое в случае сбоя. Это очень сложная планка, потому что всегда есть один жизнеспособный вариант: выйти изящно.
источник