【问题标题】:setlocale raising errno while a non-NULL value is returnedsetlocale 在返回非 NULL 值时引发 errno
【发布时间】:2015-11-12 09:58:52
【问题描述】:

考虑以下简约代码片段:

#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,如果进程环境(LC_ALL等)指定的locale不可用,setlocale返回NULL(即对应文件丢失,需要用localegen生成)或类似的工具)。测试表明在这种情况下errno 设置为ENOENT,但在 Debian 7 框之一上,setlocale 返回一个有效的非NULL 语言环境字符串errno 设置为ENOENT

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

如何进一步诊断上述病例?

【问题讨论】:

  • 确保在调用setlocale 之前将errno 设置为零 因为setlocale(就像所有POSIX 函数一样)如果成功,将保持errno 不变.
  • @FUZxxl:谢谢,忘记了,但是在setlocale 调用之前将errno 设置为0 并不会改变整体情况:我仍然得到ENOENTEILSEQ 有效且可用语言环境。
  • @FUZxxl 这根本不是真的。 POSIX 明确表示:“未指定成功调用函数后的 errno 设置,除非该函数的描述指定不应修改 errno。” setlocale 并没有说不会改变errno,所以是允许的。
  • @Art 好吧,我好像在这方面被误导了。

标签: c posix locale errno setlocale


【解决方案1】:

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

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

【讨论】:

  • 我很确定 Cygwinsetlocale 实现会引发 EILSEQ,因为底层调用 iconv
  • 更准确地说: errno 的值只能在调用明确声明要设置的函数之后定义,直到它被下一个函数调用更改或如果应用程序为其分配了一个值。 errno 的值只有在函数的返回值表明它是有效的时候才应该被检查。 pubs.opengroup.org/onlinepubs/9699919799/functions/… 因为大多数函数都指定了类似 的东西,所以函数应该返回 -1 并设置 errno 来指示错误(来自read),errno 将是不确定的,除非调用失败。
  • 所以在这种情况下,由于setlocale()调用成功时没有显式设置errno,所以errno的值是未定义的。即使在调用 setlocale() 之前将其设置为零,也不能保证其在调用之后的值。
【解决方案2】:

一般来说,当涉及到来自 POSIX 的大多数函数时,除非函数返回错误,否则您不应该查看 errno

POSIX 记录它here

只有在函数的返回值指示它有效时,才应检查 errno 的值。

即使函数成功,也允许更改errno

函数调用成功后errno的设置是未指定的,除非该函数的描述指定不应该修改errno。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-16
    • 2014-10-08
    • 2014-11-24
    • 1970-01-01
    • 2017-08-14
    • 2022-06-28
    • 1970-01-01
    相关资源
    最近更新 更多