根据Microsoft文档,APIchar * strerror(int errnum)
"将errnum映射到错误消息字符串,并返回指向字符串";
在实践中,有一个重要方面没有被描述所涵盖:
- 在Windows上,使用相同的指针表示不同的errnum值。它导致了意外的行为:strerror((在一个printf((中具有不同的errnum,显示相同的值
这是表示这种行为的代码:
printf("---------- print separately [OK] ----------n");
printf("strerror(1)=(%s), ptr=(%p)n", strerror(1), strerror(1));
printf("strerror(2)=(%s), ptr=(%p)n", strerror(2), strerror(2));
printf("---------- print together [BUG] ----------n");
printf("strerror(1)=(%s), strerror(2)=(%s)n", strerror(1), strerror(2));
printf("ptr_1=(%p), ptr_2=(%p)n", strerror(1), strerror(2));
printf("---------- print together, get pointers separately [BUG] ----------n");
const char * s1 = strerror(1);
const char * s2 = strerror(2);
printf("strerror(1)=(%s), strerror(2)=(%s)n", s1, s2);
printf("ptr_1=(%p), ptr_2=(%p)n", s1, s2);
printf("---------- --------------------------- ----------n");
输出:
---------- print separately [OK] ----------
strerror(1)=(Operation not permitted), ptr=(000002756DE6BD90)
strerror(2)=(No such file or directory), ptr=(000002756DE6BD90)
---------- print together [BUG] ----------
strerror(1)=(Operation not permitted), strerror(2)=(Operation not permitted)
ptr_1=(000002756DE6BD90), ptr_2=(000002756DE6BD90)
---------- print together, get pointers separately [BUG] ----------
strerror(1)=(No such file or directory), strerror(2)=(No such file or directory)
ptr_1=(000002756DE6BD90), ptr_2=(000002756DE6BD90)
---------- --------------------------- ----------
未使用编译器优化标志。同样的代码在带有gcc的Linux上也能正常工作。
更新:问题已得到回答。
Q: 这种行为是BUG吗?
- 编号
问:有没有其他更好的API可以在Linux上作为strerror工作?
- 是。strerror_s
这不是一个bug。根据strerror:的POSIX文档
返回的字符串指针可能无效,或者字符串内容可能被随后对
strerror()
的调用覆盖
因此,如果您在一个语句中调用它两次,则不应期望其中一个以上的值是有效的(并且您的代码崩溃或做其他奇怪的事情是完全可以接受的(。
潜在的原因可能是Microsoft C库为strerror
返回值使用了一个固定的缓冲区,因此每次调用都会覆盖以前保存的值。这是标准特别允许的,以便于实施。
strerror
返回的内存可以通过对函数的后续调用进行修改。
相反,使用strerror_s
,它允许您提供一个缓冲区来将错误字符串写入。
char s1[200], s2[200];
strerror_s(s1, sizeof s1, 1);
strerror_s(s2, sizeof s2, 2);
使用GNU C库,您会遇到与strerror()
相同的问题,即使用内部静态缓冲区,该缓冲区使用与作为参数传递的错误代码相对应的字符串进行更新。这就是为什么strerror()
不是多线程安全的。MT安全版本是strerror_r()
,它将错误字符串放入用户分配的缓冲区,而不是内部静态缓冲区。