关于 errno 和 strerror()

4

考虑以下代码中,fopen()失败的情况:

FILE *fp = fopen("file.txt", "w");
if (fp == NULL) {
    printf("Error occurred while opening file, errno=%d, %s\n", 
                         errno, strerror(errno));
    exit(1);
}

由于在C语言中函数参数的求值顺序是未指定的,因此在调用printf()时,如果首先对strerror()进行调用并且失败,则当行实际打印时,errno会被设置为其他内容吗?还是说errno已经被复制到printf()的激活记录中,甚至在评估strerror()之前就已经保持不变了?这是未指定的行为吗? 编辑: 是的,我确实明白我可以在fopen()之后立即将errno保存到某个int中,但这不是我的重点。我试图弄清楚上面的代码段如何运行。

1
如果我没记错的话(可能我记错了),strerror() 函数不会设置 errno。但这是一个好问题。一个安全的方法是先捕获 int errnum = errno;,然后使用 errnum - Jonathan Leffler
3
成功完成后,strerror()函数将返回指向生成的消息字符串的指针。如果出现错误,可能会设置errno值,但没有保留任何返回值来指示错误。不确定这是否在标准中,还是只在POSIX中存在。 - Ted Lyngmo
2
@WilliamPursell 同意不打印文件名 - 仅针对部分错误号。根据使用的语言环境,文本可能会被翻译('字符串内容是特定于语言环境的。')。数字保持不变。在我个人看来,在这些我已经熟知的情况下,我更喜欢阅读错误数字。承认如果它们依赖于操作系统或其他因素,它们会变得不那么有用... - Aconcagua
1
@WilliamPursell 考虑将实际的 errno 值打印为用户将放入错误报告中的消息文本的校验和。这根本不是“线路噪声”。 - Andrew Henle
1
@WilliamPursell 即使在 Linux 中,也不是所有的架构都使用相同的 errno 映射。 - Ian Abbott
显示剩余10条评论
3个回答

5
由于没有保留返回值来指示错误,希望检查错误情况的应用程序应将errno设置为0,然后调用strerror(),然后检查errno。同一篇文章中提到很可能是未指定的行为。MAN也是这样说的。
POSIX.1-2001和POSIX.1-2008要求成功调用strerror()或strerror_l()后errno保持不变,并指出由于没有函数返回值来指示错误,因此希望检查错误的应用程序应在调用之前将errno初始化为零,然后在调用之后检查errno。
所以可以保存fopen的errno,然后获取strerror的errno。或者直接使用perror。

3

Considering fopen fails, in the following piece of code:

FILE *fp = fopen("file.txt", "w");
if (fp == NULL) {
    printf("Error occurred while opening file, errno=%d, %s\n", 
                         errno, strerror(errno));
    exit(1);
}

Since the order in which function arguments are evaluated is unspecified in C, while invoking printf, in case call to strerror is evaluated (invoked) first and it fails, wouldn't errno be set to something else when the line actually gets printed?

这是完全有可能的。此外,根据C标准,无论是否在调用strerror或任何其他库函数时出现错误,只要未为该函数记录使用errno(请参见C17 7.5/3),都可以将errno设置为非零值。但是,POSIX(自POSIX.1-2001以来,在1997年的Single UNIX规范版本2之前)保证,如果成功,则strerror不会更改errno的设置。
还是说,即使在评估strerrno()之前,errno也已经被复制到printf的激活记录中,因此不会发生变化?这是未指定的行为吗?
这是未指定的行为。参数和函数指示符可以按任何顺序进行评估(但在实际调用函数之前存在一个顺序点,以避免在函数体内的评估与在另一个函数体内的评估交错)(请参见C17 6.5.2.2/10)。

1

这是一个在Linux下可能会发生的问题,因为strerror()手册注释部分中说errno可以被服务修改:

POSIX.1-2001允许strerror()在调用遇到错误时设置errno,但并没有指定在出现错误时应该返回什么值作为函数结果。在一些系统上,如果错误号码未知,则strerror()返回NULL。在其他系统上,如果错误号码未知,则strerror()返回像“发生错误nnn”这样的字符串,并将errno设置为EINVAL。C99和POSIX.1-2008要求返回值必须为非NULL。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接