c-在一个printf()中使用两次具有不同值的strerror()会在Windows上触发一个错误



根据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(),它将错误字符串放入用户分配的缓冲区,而不是内部静态缓冲区。

最新更新