错误 C4996: 'ctime': 此函数或变量可能不安全

11

我正在进行一个有关静态源代码分析的大型项目,所有内容都能够成功编译,除了一件事。我已经在标题中提供了错误信息。困扰我的是它给出一个说不安全的错误信息。我原以为它应该只是一个警告而不是一个错误。顺便说一下,我正在使用Visual Studio 2012。以下是我在ctime中遇到错误的部分代码。如果有人可以帮助我解决这个错误,我会很高兴。

void CppCheckExecutor::reportProgress(const std::string &filename, const char stage[], const std::size_t value)
{
     (void)filename;

     if (!time1)
         return;

     // Report progress messages every 10 seconds
     const std::time_t time2 = std::time(NULL);
     if (time2 >= (time1 + 10)) {
         time1 = time2;

         // current time in the format "Www Mmm dd hh:mm:ss yyyy"
         const std::string str(std::ctime(&time2));

         // format a progress message
         std::ostringstream ostr;
         ostr << "progress: "
              << stage
              << ' ' << value << '%';
         if (_settings->_verbose)
             ostr << " time=" << str.substr(11, 8);

         // Report progress message
         reportOut(ostr.str());
     }
}
5个回答

13
如果您确定代码没有安全问题,您可以通过#pragma warning(disable : 4996)来禁用该警告。

11
如果你查看ctime的描述,你会注意到:
此函数返回指向静态数据的指针,不是线程安全的。此外,它修改了可能与gmtime和localtime共享的静态tm对象。POSIX将此函数标记为过时,并建议使用strftime。
对于导致字符串超过25个字符(例如10000年)的time_t值,行为可能未定义。
...这些都是需要担心的事情。
另一方面,如果你查看strftime
size_t strftime(char* str, size_t count, const char* format, tm* time);
返回值
成功写入字符数组str指向的字节数,不包括终止符'\0'。如果在存储整个字符串之前达到了count,则返回0并且内容未定义。
所有参数都是明确的,因此您完全控制可能发生的数据竞争,并且也没有溢出提供的缓冲区的风险。
这是C的方式,而C++引入了,其中特定函数std::put_time也可以用于将时间输出到流中:
#include <iostream>
#include <iomanip>
#include <ctime>
#include <chrono>

int main() {
    std::time_t const now_c = std::time();
    std::cout << "One day ago, the time was "
              << std::put_time(std::localtime(&now_c), "%F %T") << '\n';
}

这样做甚至更好,因为您不再需要担心可能的缓冲区溢出问题。


感谢chrono和put_time! - user1726549
2
请注意,std::localtime 也会返回指向静态数据的指针,可能不是线程安全的,因此这仅修复了可能的缓冲区溢出问题。 - interjay
@interjay:没错,这个C++的示例只涉及到打印部分;不幸的是,我不知道有没有既符合标准又线程安全的从time_tstruct tm的转换函数。 - Matthieu M.
5
对于 VS 2017,构造函数有一些变化:std::time_t const now_c = std::time(NULL); (说明:该代码行是在定义一个名为now_c的常量并将其初始化为当前时间的UNIX时间戳所对应的std::time_t类型值。) - ingconti
@NicholasJela:我这里没有看到关于put_time的任何警告。它有什么不安全之处? - Matthieu M.
显示剩余3条评论

5

是的,它应该只是一个警告,而不是错误。 要获得简单的警告而不是错误,请在VS项目中禁用SDL检查(在“配置属性” -> “C / C ++” -> “常规”选项卡中)。


根据http://en.cppreference.com/w/c/chrono/ctime,该函数已经过时。POSIX和C标准建议使用`strftime`代替。 - lmiguelmh

4

std::ctime由于两个原因不是线程安全的:

  • 它可能会修改一个被多个函数共享的类型为std::tm的全局对象。
  • 它修改了一个全局的char数组并返回该数组的指针。

如果您有其他线程调用std::gmtimestd::localtime或者std::ctime,那么可能会出现冲突。

最好的做法是将对std::ctime的调用转换为对std::strftime的调用。这与POSIX一致,它认为ctime已经过时,并建议使用strftime来代替。


1

vs 2017:

#include "stdafx.h"


#include <iostream>
#include <iomanip>
#include <ctime>
#include <chrono>

int main() {
    std::time_t const now_c = std::time(NULL);
    auto s = std::put_time(std::localtime(&now_c), "%F %T");
    std::cout << s << std::endl;
}

但你仍然会收到以下警告:

....cpp(31): 警告 C4996: 'localtime':此函数或变量可能不安全。考虑使用 localtime_s 代替。要禁用弃用,请使用 _CRT_SECURE_NO_WARNINGS。有关详细信息,请参见在线帮助。

为了预防,您可以使用以下方法:

errno_t err;
struct tm time_info;
time_t time_create = time(NULL);
localtime_s(&time_info, &time_create);
char timebuf[26];
err = asctime_s(timebuf, 26, &time_info);

从MSDN部分提取的纯C代码......老方法..


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