Почему при вызове функции crypt() из unistd.h для errno устанавливается значение ENOENT?
Я написал и запустил следующий код:
#define _XOPEN_SOURCE
#include <iostream>
#include <unistd.h>
int main()
{
std::cout << "errno = " << errno << std::endl;
std::cout << crypt("sometext", "ab") << std::endl;
std::cout << "errno = " << errno <<std:: endl;
return 0;
}
Начальная стоимость errno
было 0
, но после звонка на crypt()
функция была установлена на 2
(ENOENT).
Вот вывод:
errno = 0
abtAunjzvWWRQ
errno = 2
1 ответ
Вот что говорит стандарт C errno
(§7.5, параграф 3, выделение добавлено.)
Значение
errno
в начальном потоке ноль при запуске программы (начальное значениеerrno
в других потоках это неопределенное значение), но никогда не устанавливается в ноль какой-либо библиотечной функцией. Значениеerrno
может быть установлен в ненулевое значение при вызове библиотечной функции независимо от того, есть ли ошибка, при условии использованияerrno
не задокументировано в описании функции в этом международном стандарте.
И вот (часть), что говорит Posix (снова, акцент добавлен):
Значение errno
следует проверять только в том случае, если указано, что оно является допустимым возвращаемым значением функции.… Никакая функция в этом томе POSIX.1-2008 не должна устанавливать errno
до 0. Настройка errno
после успешного вызова функции не определено, если в описании этой функции не указано, что errno
не должны быть изменены.
crypt
является функцией Posix (о чем свидетельствует ее присутствие в unistd.h
). В описании не указано, что errno
не должны быть изменены. Так может быть и было.
Короче говоря, никогда не пытайтесь использовать значение errno
если функция явно не сообщила об ошибке, и эта функция задокументирована для установки errno
, И в этом случае убедитесь, что вы используете его (или сохраняете его значение) сразу после вызова этой функции и перед тем, как делать что-либо еще, что может установить errno
(который включает в себя использование iostreams
а также cstdio
).
Все это может показаться странным в отдельности, но на самом деле это имеет смысл. Рассмотрим, например, функцию, которая должна обращаться к файлу конфигурации, если он существует. Это будет включать в себя что-то вроде:
FILE* config = fopen(configFileName, "r");
if (config) { /* Read the file */ }
else { /* Set default values */ }
Если файл конфигурации не существует, он просто не используется. Нет проблем. Но errno
вполне могли быть установлены fopen
отказ.
Подобные вещи довольно распространены в библиотечных функциях, которые выполняют инициализацию при первом вызове. Если бы не это положение, любая библиотечная функция, которая вызывала другую библиотечную функцию, должна была бы тщательно сохранить errno
перед его запуском, а затем восстановите в конце, если не сообщалось о фактической ошибке. Бьюсь об заклад, ваши функции не делают этого:) - мои, конечно, нет. Это неудобно и подвержено ошибкам. Лучше и более убедительным является принятая конвенция: errno
допустимо только в том случае, если функция определенно сообщила об ошибке.