setlocale 在返回非 NULL 值时引发 errno

setlocale raising errno while a non-NULL value is returned

考虑以下简约代码片段:

#include <stdio.h>
#include <errno.h>
#include <locale.h>

int main() {
    const char *locale = setlocale(LC_ALL, "");
    if (errno == ENOENT) {
        printf("Locale %s unavailable.\n", locale ? locale : "<unknown>");
    } else if (errno) {
        printf("setlocale() failed: errno = %d\n", errno);
        perror("setlocale() failed");
    }

    if (locale) {
        printf("Current locale: %s\n", locale);
    }

    return errno;
}

这在许多 UNIX 机器上成功运行(Solaris 8、Debian Linux 8、OpenBSD 5.8)。但是,也有一些例外。

根据 manual page, setlocale returns NULL if locale specified by the process environment (LC_ALL et al.) is unavailable (i. e. the corresponding files are missing and need to be generated with localegen or a similar tool). Testing reveals that errno 在这种情况下设置为 ENOENT,但在 Debian 7 框之一 setlocale returns 有效的非 NULL 语言环境字符串 anderrno 设置为 ENOENT.

另一个奇怪的行为是由 Cygwin 表现出来的:对于每个单字节西里尔语言环境(ru_RU.CP1251ru_RU.CP866ru_RU.KOI8-R, ru_RU.ISO-8859-5), setlocale returns 正确的语言环境字符串 errno 设置为 EILSEQ。同时ru_RU.UTF-8不受影响

以上情况如何进一步确诊?

POSIX does not specify setlocale 如何影响 errno(请参阅错误部分,其中显示“未指定错误”)。在 setlocale 调用后,您可以安全地忽略 errno 的值。

setlocale 的实现永远不应触及 errno,但您测试的某些实现似乎在这方面存在错误。我建议您针对提供错误 setlocale 实施的项目提交错误报告。

一般来说,对于 POSIX 中的大多数函数,您不应该查看 errno,除非该函数返回错误。

POSIX 记录它 here

The value of errno should only be examined when it is indicated to be valid by a function's return value.

函数允许更改 errno 即使它们成功了:

The setting of errno after a successful call to a function is unspecified unless the description of that function specifies that errno shall not be modified.

这种情况很常见,函数尝试一件事失败了,尝试另一件事又成功了。在库中围绕系统调用保留 errno 通常太昂贵或太难。

查看 errno 而函数没有向您发出错误信号是一个错误,您不能那样做。