почему fopen() или open () используют errno вместо простого возврата кода ошибки?
в обычном случае open()
возвращает новый дескриптор файла, или -1, если произошла ошибка и в этом случае errno
правильно настроена.
Я не понимаю, почему этот механизм errno
здесь используется? какова цель этого? почему просто мы не можем сопоставить все ошибки с некоторым отрицательным возвратом нет?
как
fd = open("/dev/tty0", O_RDWR | O_SYNC);
if(fd == -1)
printf("this is EACCES error");
else if (fd == -2)
printf("this is EPERM error");
есть ли какая-либо польза errno
механизм.? если да, то я хотел бы знать / понимать, то в других вещах я также могу использовать этот механизм.
4 ответов
С fopen
возвращает FILE*
вы не можете ожидать, что он вернет код ошибки в этом указателе: единственное "специальное" значение для указателей -0
.
как вы заметили, для open
это ограничение не действует. На самом деле системы как linux делают именно то, что вы предлагаете на их более низких уровнях. Системный вызов под капотом возвращает отрицательный код ошибки, если что-то пойдет не так. Этот (отрицаемый) код затем подключается к errno
мелкой оболочкой пространства пользователя, которая затем возвращает -1
для указания ошибки в приложении.
причина, по которой это делается, чисто историческая. В старые добрые времена не было ниток и errno
все еще была простой глобальной переменной. В то время выбранная стратегия несла не так много накладных расходов и, вероятно, казалась приемлемым способом связи между ОС и приложением. Поскольку такие интерфейсы в основном не могут быть изменены без нарушения большого количества кода, мы застрянем с errno
как псевдо-переменная, которая поток локальный.
это не идеально, но накладные расходы не так плохо, как это звучит, так как они явно признаки того, что должно происходить только в исключительных случаях.
для меня преимущество в том, что получение информации об ошибке унифицировано таким образом, возврат некоторого отрицательного значения будет работать нормально с open
как он возвращает целое число, но fopen
возвращает FILE *
поэтому там придется использовать другую технику.
errno
- Это код ошибки. Важно сопоставить ошибки с тем, что происходит на самом деле, чтобы вы могли принимать стратегические решения в своем коде о том, что делать дальше. Например, ERANGE
который определен в errno.h
, скажет вам, что результат strtol("0xfffffffff",NULL,0)
находится вне диапазона этой функции. Что еще более важно в вашем примере, хорошо знать, есть ли у вас EACCES
или EPERM
ошибки, чтобы вы знали, как обрабатывать файл.
вы не можете сопоставить все проблемы с одним кодом ошибки поскольку у вас может быть несколько проблем, которые вы хотели бы поймать и обработать. Когда я говорю поймать, я не имею в виду try/catch.
использование errno устанавливает и механизм обработки ошибок, поэтому вы получаете больше информации, чем просто -1.
ERANGE, EACCES, EPERM и другие считаются макросами, которые сопоставляются с определенным номером ошибки для удобства.
предоставление каждой функции определенного набора возвращаемых значений делает слишком сложным написание кода в общем виде. С текущей семантикой вы можете принять универсальный шаблон:
int fd;
if ((fd = some_function(arg1, arg2)) == -1)
{
perror("some_function");
exit(1);
}
вы даже можете обернуть это в макросе:
#define CALL_OR_DIE(function, ret, ...) \
if ((ret = function(__VA_ARGS__)) == -1) \
{ perror(#function); exit(1); }
использование:
int fd;
CALL_OR_DIE(open, fd, "/dev/tty0", O_RDWR | O_SYNC);