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

Posted

技术标签:

【中文标题】错误 C4996:“ctime”:此函数或变量可能不安全【英文标题】:error C4996: 'ctime': This function or variable may be unsafe 【发布时间】:2012-11-13 02:30:14 【问题描述】:

我有一个关于静态源代码分析的大项目,一切都编译成功,除了一件事。我在标题中提供了错误消息。让我感到困惑的是,它给出了一条错误消息,说不安全。我认为这应该只是警告,而不是错误。顺便说一句,我使用的是 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());
     

【问题讨论】:

【参考方案1】:

如果您查看ctime 的描述,您会注意到:

此函数返回指向静态数据的指针,并且不是线程安全的。此外,它还修改了静态tm 对象,可以与gmtime 和localtime 共享。 POSIX 将此函数标记为已过时,并推荐使用 strftime。

行为可能未定义,因为 time_t 的值导致字符串长度超过 25 个字符(例如 10000 年)

...有很多事情需要担心。

另一方面,如果你看看strftime:

size_t strftime(char* str, size_t count, const char* format, tm* time);

返回值

写入 str 指向的字符数组的字节数,不包括成功时的终止 '\0'。如果在可以存储整个字符串之前达到计数,则返回​0​并且内容未定义。

所有参数都是显式的,因此您可以完全控制可能的数据竞争,并且也不存在溢出缓冲区的风险。

虽然这是 C 方式,但 C++ 引入了 &lt;chrono&gt;,其中特定函数 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! 请注意,std::localtime 还返回一个指向静态数据的指针,并且可能不是线程安全的,因此这只能修复可能的缓冲区溢出。 @interjay: 对,C++ 示例只涉及打印部分;不幸的是,我不知道从 time_tstruct tm 的转换函数既是标准的又是线程安全的。 对于 VS 2017 的构造函数有所改变:std::time_t const now_c = std::time(NULL);【参考方案2】:

如果您确定您的代码中没有安全问题,您可以通过#pragma warning(disable : 4996) 禁用此功能。

【讨论】:

【参考方案3】:

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

【讨论】:

根据en.cppreference.com/w/c/chrono/ctime,此功能已过时。 POSIX 和 C 标准推荐使用strftime【参考方案4】:

std::ctime 不是线程安全的,原因有两个:

它可以修改多个函数共享的std::tm 类型的全局对象。 它修改一个全局char 数组并返回一个指向该数组的指针。

如果您有其他线程调用 std::gmtimestd::localtimestd::ctime,则可能会发生冲突。

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

【讨论】:

【参考方案5】:

与 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);

plain C 部分取自 MSDN...旧方式..

【讨论】:

以上是关于错误 C4996:“ctime”:此函数或变量可能不安全的主要内容,如果未能解决你的问题,请参考以下文章

此函数或变量可能是不安全的 Visual Studio

windows系统下VS2013或者VS2017的C4996错误解决方法

Visual Studio错误:strcpy出现C4996错误的解决

localtime vs localtime_s 和适当的输入参数

关闭VS警告 warning C4996

vs2013/2015中scanf函数类似于error C4996: 'scanf': This function or variable may be unsafe的安全检查错误