Почему при вызове функции 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 допустимо только в том случае, если функция определенно сообщила об ошибке.

Другие вопросы по тегам