【问题标题】:Why does a call to the crypt() function from unistd.h set the errno to ENOENT?为什么从 unistd.h 调用 crypt() 函数会将 errno 设置为 ENOENT?
【发布时间】:2018-03-17 19:26:59
【问题描述】:

我已经编写并运行了以下代码:

#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

【问题讨论】:

  • crypt 库可能试图在途中打开某种资源文件,但失败了,但 crypt 库不认为这是一个问题。 (也许它有一个 B 计划。)另请参阅 question 12.24 中的 C FAQ list
  • crypt 的手册页没有提到任何关于设置errno 的内容,它只是说成功时,返回指向加密密码的指针。出错时,返回 NULL。。在您的情况下,它不会返回 NULL,因此函数成功结束。 errno 必须设置在 crypt 内部的某个位置,并且函数知道如何处理错误(请注意,函数不必手动将 errno 设置为 0)。
  • 这很常见,这就是为什么将errno 设置为0、调用函数然后检查errno推荐的事情。通常只有在库函数通过其返回值指示它失败后检查 errno 才有意义——并且只有当它是一个记录为设置 errno 的库函数时才有意义。
  • 虽然不是这种情况的根本原因,但cout 语句中发生的任何流操作都可以轻松更改errno 的值......
  • 这里没有证据表明crypt() 做到了,因为它与流 I/O 交错;并且没有任何理由不应该打扰errno。仅在前一个系统调用返回 -1 时检查它才有效。

标签: c++ c crypt unistd.h


【解决方案1】:

以下是 C 标准对 errno 的说明(第 7.5 节第 3 段,已添加重点。)

程序启动时初始线程中errno的值为零 (其他线程中errno的初始值是不确定的 value),但绝不会被任何库函数设置为零。 价值 的errno 可以通过库函数调用设置为非零,无论是 没有错误,前提是没有记录使用errno 在本国际标准的功能描述中。

这是Posix says 的(部分)内容(再次强调):

只有当函数的返回值表明errno 的值是有效的时,才应该检查它的值。… POSIX.1-2008 卷中的任何函数都不应将errno 设置为0。未指定成功调用函数后errno 的设置,除非该函数的描述指定不应修改errno

crypt 是一个 Posix 函数(如其在 unistd.h 中的存在所示)。描述中没有指定errno 不应被修改。可能是这样,而且确实如此。

简而言之,从不尝试使用 errno 的值,除非函数已明确报告错误并且该函数已记录为设置 errno。在这种情况下,请确保在调用该函数后立即使用它(或保存其值),并且在执行任何其他可能设置errno(包括使用iostreamscstdio)之前。

这可能看起来有点奇怪,但它实际上是完全合理的。例如,考虑一个需要查阅配置文件(如果存在)的功能。它将包含如下代码:

FILE* config = fopen(configFileName, "r");
if (config) { /* Read the file */ }
else { /* Set default values */ }

如果配置文件不存在,它就不会被使用。没问题。但是errno 很可能是由fopen 失败设置的。

这种事情在库函数中很常见,它们在第一次调用时执行初始化。如果没有这个规定,任何调用另一个库函数的库函数都必须在启动之前小心保存errno,然后在结束时恢复它,除非报告了实际错误。我敢打赌你的函数不会那样做:)——我的肯定不会。它繁琐且容易出错。更好、更可审计的是实际采用的约定:errno 仅在函数确定报告错误时才有效。

【讨论】:

    猜你喜欢
    • 2017-05-29
    • 2018-07-31
    • 1970-01-01
    • 2018-02-02
    • 1970-01-01
    • 1970-01-01
    • 2015-06-05
    • 2017-08-29
    • 1970-01-01
    相关资源
    最近更新 更多