为什么我不能使用strerror?

Jam*_*ieH 31 c c++ deprecated

我正在将一些代码移植到Windows,而Microsoft编译器(Visual C++ 8)告诉我这strerror()是不安全的.

撇开微软所有安全字符串中的烦恼因素,我实际上可以看到一些已弃用的函数是危险的.但我无法理解可能出现的问题strerror().它需要一个code(int),并返回相应的字符串,如果该代码未知,则返回空字符串.

危险在哪里?

在C中有一个很好的选择吗?

C++中有一个很好的替代品吗?

[编辑]

得到了一些很好的答案,现在理解一些实现可能疯狂到实际写入一个共同的共享缓冲区 - 在单线程中不安全的重入,没关系线程之间! - 我的问题不再是"为什么我不能使用它,有什么替代方案?" "C和/或C++中是否有任何体面,简洁的替代品?"

提前致谢

dfa*_*dfa 24

strerror不推荐使用,因为它不是线程安全的.strerror适用于内部静态缓冲区,可能被其他并发线程覆盖.您应该使用一个名为的安全变体strerror_s.

安全变体要求将缓冲区大小传递给函数,以便在写入之前验证缓冲区是否足够大,从而有助于避免可能允许恶意代码执行的缓冲区溢出.

  • 他_did_告诉你为什么.它适用于内部静态缓冲区 - 即跨线程共享的缓冲区.那不安全. (14认同)
  • 这都是关于语言环境的 - 即使您更改了语言环境(可能会卸载旧的语言环境),从strerror返回的字符串仍需要保持有效,直到再次调用strerror为止.因此,将指针返回到语言环境数据意味着实现需要保持旧语言环境不变.我认为这是可行的,但并不像你想象的那么简单. (7认同)
  • 呃,问题是:_为什么它被认为是不安全的?你没告诉我什么. (4认同)
  • 是的。我很抱歉。现在,对整个世界来说,下一个问题是:为什么有人会像那样实现它呢? (3认同)
  • JamieH,该函数可以有效地实现为char*msg [] = {"找不到文件","你病了","你不是根","时间结束了"}; char*strerror(int n){return msg [n]; } (2认同)

dwc*_*dwc 19

strerror本身并不是不安全的.在线程之前的过去,它根本不是问题.对于线程,两个或多个线程可以调用,strerror使返回的缓冲区处于未定义状态.对于单线程程序,strerror除非他们在libc中玩一些奇怪的游戏,例如DLL中的所有应用程序的公共内存,否则不应该使用它.

为了解决这个问题,有一个相同功能的新界面:

int strerror_r(int errnum, char *buf, size_t buflen);
Run Code Online (Sandbox Code Playgroud)

请注意,调用者提供缓冲区空间和缓冲区大小.这解决了这个问题.即使对于单线程应用程序,您也可以使用它.它不会有点伤害,你也可以习惯这样做更安全.

注意:上面的原型是XSI规范.它可能因平台或编译器选项或#define符号而异.例如,GNU根据a使得它或它们自己的版本可用#define

  • 还有TR24731,它定义了strerror_s()。 (2认同)

Bas*_*ard 16

得到了一些很好的答案,现在理解一些实现可能疯狂到实际写入一个共同的共享缓冲区 - 在单线程中不安全的重入,没关系线程之间! - 我的问题不再是"为什么我不能使用它,有什么替代方案?" "C和/或C++中是否有任何体面,简洁的替代品?"

Posix指定strerror_r(),在Windows上你可以使用strerror_s(),这有点不同但目标相同.我这样做:

#define BAS_PERROR(msg, err_code)\
  bas_perror(msg, err_code, __FILE__, __LINE__)

void bas_perror (const char* msg, int err_code, const char* filename,
                 unsigned long line_number);


void
bas_perror (const char* usr_msg, int err_code, const char* filename,
            unsigned long line_number)
{
  char sys_msg[64];

#ifdef _WIN32
  if ( strerror_s(sys_msg, sizeof sys_msg, err_code) != 0 )
  {
    strncpy(sys_msg, "Unknown error", taille);
    sys_msg[sizeof sys_msg - 1] = '\0';
  }
#else
  if ( strerror_r(err_code, sys_msg, sizeof sys_msg) != 0 )
  {
    strncpy(sys_msg, "Unknown error", sizeof sys_msg);
    sys_msg[sizeof sys_msg - 1] = '\0';
  }
#endif

  fprintf(stderr, "%s: %s (debug information: file %s, at line %lu)\n",
          usr_msg, sys_msg, filename, line_number);
}
Run Code Online (Sandbox Code Playgroud)

我编写了这个函数,因为Posix线程函数不会修改errno,而是返回错误代码.所以这个函数基本上是相同的perror(),除了它允许你提供除了之外的错误代码errno,并且还显示一些调试信息.您可以根据自己的需要进行调整.


bee*_*f2k 5

您不能依赖返回的字符串,strerror()因为它可能会随着对函数的下一次调用而改变.之前返回的值可能会过时.特别是在多线程环境中,您无法确保在访问字符串时该字符串是有效的.

想象一下:

Thread #1:
char * error = strerror(1);
                                    Thread #2
                                    char * error = strerror(2);
printf(error);
Run Code Online (Sandbox Code Playgroud)

根据实现strerror(),此代码打印出错误代码2的错误代码,而不是错误代码1.

  • 它会以什么样的机制改变?据推测,字符串是静态定义的 - 在C运行时库的实现内部,因此永远不会改变?或者这是错误的,他们可能确实在动态变化? (7认同)
  • 主要问题是Posix不要求strerror()是线程安全的.你必须使用strerror_r().在Windows上使用strerror_s(). (3认同)