C++ 为什么可以';我不能用strerror吗?

C++ 为什么可以';我不能用strerror吗?,c++,c,deprecated,C++,C,Deprecated,我将一些代码移植到Windows,微软编译器(Visual C++ 8)告诉我 StruError()/Cyto>不安全。 撇开微软所有安全字符串中的烦人因素不谈,我可以看到一些不推荐使用的函数是危险的。但是我不明白strerror()有什么问题。它接受一个代码(int),并返回相应的字符串,如果该代码未知,则返回空字符串 危险在哪里 在C语言中有好的选择吗 在C++中有一个很好的选择吗? [编辑] 已经有了一些很好的答案,现在了解到一些实现可能疯狂到实际写入公共共享缓冲区-在单个线程内重新进入

我将一些代码移植到Windows,微软编译器(Visual C++ 8)告诉我 StruError()/Cyto>不安全。 撇开微软所有安全字符串中的烦人因素不谈,我可以看到一些不推荐使用的函数是危险的。但是我不明白
strerror()
有什么问题。它接受一个代码(
int
),并返回相应的字符串,如果该代码未知,则返回空字符串

危险在哪里

在C语言中有好的选择吗

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

[编辑]

已经有了一些很好的答案,现在了解到一些实现可能疯狂到实际写入公共共享缓冲区-在单个线程内重新进入不安全,更不用说线程之间了我的问题不再是“为什么我不能使用它,还有什么替代方案?”“在C和/或C++中有没有合适的简洁的替代方案?”
“提前感谢”

strerror
已被弃用,因为它不是线程安全的
strerror
在内部静态缓冲区上工作,该缓冲区可能被其他并发线程覆盖。您应该使用名为
strerror\s
的安全变体


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

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

想象一下:

Thread #1:
char * error = strerror(1);
                                    Thread #2
                                    char * error = strerror(2);
printf(error);

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

虽然我不知道Microsoft的原因,但我注意到strerror返回一个非常量字符*,这意味着有一种风险,那就是在你之前,某个快乐的恶作剧者已经给strerror打过电话并修改了消息。

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

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

int strerror_r(int errnum, char *buf, size_t buflen);
请注意,调用者提供了缓冲区空间和缓冲区大小。这就解决了问题。即使对于单线程应用程序,也可以使用它。一点也不疼,你最好习惯用更安全的方式


注:以上原型来自POSIX规范。它可能因平台而异,也可能因编译器选项或定义符号而有所不同。例如,GNU根据
#define

提供该版本或其自己的版本。对于简洁的包装器,您可以使用的
stlsoft::error\u desc
,如下所示:

std::string errstr = stlsoft::error_desc(errno);
看一下代码,它似乎是按照
strerror()
实现的,这意味着它在线程中可以安全地重新进入(即,如果在给定语句中多次使用),但它没有解决多线程问题

他们似乎对缺陷进行了非常快速的发布周期,所以你可以试着请求一个mod

已经有了一些很好的答案,现在了解到一些实现可能疯狂到实际写入公共共享缓冲区-在单个线程内重新进入不安全,更不用说线程之间了我的问题不再是“为什么我不能使用它,还有什么替代方案?”“在C和/或C++中有没有合适的简洁的替代方案?” Posix指定了
strerror\u()。我这样做:

#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);
}

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

我理解其他答案,但我认为用代码显示更清晰

检查glibc的实现(我们应该在MS-lib中得到类似的代码)

errnum
不是一种已知错误时,它必须生成类似“Unknown error 41”的字符串。此字符串不是常量,而是生成到分配的缓冲区。而且
buf
是全局变量,因此当再次使用lock调用
strerror
时,其内容可能会发生变化。这就是线程不安全的原因

另一方面,
strerror\r(int errnum,char*buf,size\t buflen)
,它会生成参数
buf
的错误字符串。因此,现在没有全球资源。这就是为什么它是线程安全的

参考:

呃,问题是:为什么它被认为是不安全的?你什么也没告诉我,他确实告诉了你原因。它在一个内部静态缓冲区上工作,即跨线程共享的缓冲区。那不安全。啊,是的。我道歉。现在,对全世界来说,下一个问题是:究竟为什么会有人这样实施它!?JamieH,该函数可以高效地实现为char*msg[]={“未找到文件”、“您生病了”、“您不是root用户”、“时间已过”};char*strerror(int n){return msg[n];}这都是关于区域设置的-从strerror返回的字符串需要保持有效,直到再次调用strerror,即使您更改了区域设置(这可能会卸载旧的区域设置)。因此,将指针返回到区域设置数据意味着实现
/* Return a string describing the errno code in ERRNUM.
   The storage is good only until the next call to strerror.
   Writing to the storage causes undefined behavior.  */
libc_freeres_ptr (static char *buf);