【问题标题】:How to get rid of "unsafe" warnings / errors in Visual Studio (strcpy, sprintf, strdup)如何摆脱 Visual Studio 中的“不安全”警告/错误(strcpy、sprintf、strdup)
【发布时间】:2011-03-20 14:41:48
【问题描述】:

我正在尝试消除一些编译器警告,这些警告说 strcpy、sprintf 等是不安全的。 我明白为什么它们不安全,但我想不出一种修复代码的好方法,以 C++ 风格。

这是代码的摘录:

extList->names[i]=(char *)malloc(length*sizeof(char));
strcpy(extList->names[i],extName);                     // unsafe
// strncpy(extList->names[i],extName,length);          // also unsafe

这是消息:

C4996: 'strcpy': 这个函数或变量可能是 不安全。考虑改用 strcpy_s。要禁用弃用,请使用 _CRT_SECURE_NO_WARNINGS。有关详细信息,请参阅在线帮助。

在不知道要复制的内容的长度的情况下,我想不出一种在 C++ 中复制数据的安全方法。 我知道有 strlen(),但这也是不安全的,因为它假设(可能不正确)数据是空终止的。

还有:

// used to concatenate:
sprintf(extStr,"%s%s",platExtStr,glExtStr);

C4996: 'sprintf': 这个函数或变量可能不安全。考虑 使用 sprintf_s 代替。要禁用弃用,请使用 _CRT_SECURE_NO_WARNINGS。有关详细信息,请参阅在线帮助。

使用 std::string 连接很容易,但是我需要以某种方式将数据放入 extStr(而不是使用 strcpy,哈哈)。 string::c_str() 函数返回一个指向不可修改数据的指针,所以我不能只设置 extStr 等于它。 (而且我什至不确定 c_str() 指针是否需要稍后调用它?它是否使用“new”分配空间?)

对这些东西有什么建议吗? 这是一个不属于我的 10,000 行文件的一部分......所以我并不热衷于用 C++ 方式重写它。

【问题讨论】:

  • 您可能应该提及您使用的操作系统、编程环境和编译器。
  • 如果是 win32/visual studio - 编译器警告甚至会告诉你如何关闭它们。
  • 10,000 行代码在一个文件中?我建议重构,然后担心清理警告。
  • @Sam Miller:“我建议重构” 虽然重构在理论上是好的,但项目可能存在时间限制。此外,当您修改代码时,总会有机会引入错误。程序员的目标是做最少的工作,解决最多的问题并获得最高的报酬。重构不合格。我参与了一些大型可怕的书面项目。在我重写了每个的 25% 之后,我决定除非它增加我的付款,否则它是不值得的。重构可能会引入新的错误,并不能解决现有问题,只会让一切“看起来”更好。
  • @J. Chomel 我认为我的编辑符合 OP 的初衷,因为 VS2010 是当时的最新版本,他的“目标”是克服这个警告(现在是一个错误),保持这个问题特定于VS 2010,当它是一个简单的编辑使其对 VS 2010 和 VS2013+ 有用时?如果不是,您认为 VS2010 和 VS2013+ 之间的差异是否值得提出一个新问题?

标签: c++ windows visual-studio debugging


【解决方案1】:

你真的不需要编译指示来禁用它们。

对于win32/msvc,在ProjectProperties -> Configuration Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions,添加如下宏:

_CRT_SECURE_NO_DEPRECATE  
_CRT_NONSTDC_NO_DEPRECATE

或者您可以在命令行参数中传递这些参数 (-D_CRT_SECURE_NO_DEPRECATE)。您可能可以在某些 *.cpp 文件的开头#define 它们。 此外,它们可能还有更多(参见 crtdefs.h - 看起来有很多......)。这类警告通常会告诉您可以使用哪些宏禁用它们 - 只需读取编译器输出即可。

【讨论】:

  • 这正是您所需要的。第一个通常会去掉所有的 *_s 函数建议。
  • _CRT_NO_MICROSOFT_JEDI_MIND_CONTROL 将是一个方便的设置。
【解决方案2】:

这是这个问题的另一个答案。

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#endif

        strcpy(destination, source);

#ifdef _MSC_VER
#pragma warning(pop)
#endif

【讨论】:

  • +1 这对我有用。 V M Rakesh 的答案没有奏效。不知道为什么。
  • 如果您使用的是预编译头文件,它可能不起作用,因为头文件可能已经包含在没有设置宏的情况下。
  • 在包含任何其他文件之前,您始终可以将其作为第一行之一放入 stdafx.h 中。不过要提醒一句:在禁用所有 C4996 警告行之前,请检查编译器提供给您的所有警告行。这个警告不是白给的。
【解决方案3】:

如果消除警告只是您的目标...只需定义此_CRT_SECURE_NO_WARNINGS,它将禁止所有弃用警告。但这不会解决不安全 CRT 函数的潜在问题。

如果您使用的是 Visual Studio 版本 >= 2005 并希望以适当的方式修复这些警告...最简单的方法是在您的项目中使用 #define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT 1

无需任何进一步的代码更改,您可以观察到大多数警告已自动修复。通过定义这个窗口将自动为大多数不安全的 CRT 函数调用安全的重载函数。静态数组的缓冲区大小是自动计算的。

虽然动态分配的缓冲区不是通过这种方式修复的,我们需要手动修复它们。详情请参考this link

以下是一种以编程方式更正示例的方法

strcpy_s(extList->names[i], length, extName); 

【讨论】:

    【解决方案4】:

    您确实知道要复制多少 - 您为它分配了空间!

    您肯定不愿意复制超出您分配的空间的内容吗?

    我更喜欢使用一种通过限制复制的项目数量来显式避免缓冲区溢出的方法。当我还是一名 C 程序员时,我们使用过

    dest = malloc(len);         // note: where did we get len?
    if ( dest is null )  panic!  // note: malloc can fail
    strncpy(dest, src, len);
    dest[len-1] =0;
    

    这有点混乱,并且有人指出使用 strncpy() 方法,该方法最初是为固定宽度字段而不是字符串设计的。但是它确实

    有诸如 strdup() 和 strlcpy() 之类的方法可以帮助我们。

    我的建议:

    1)。您的目标不应该是抑制警告,而是使代码健壮。

    2)。复制字符串时,您需要确保以下几点:

    • 保护自己免受错误输入的影响,例如未终止或过长的字符串。
    • 保护自己免受 malloc 故障的影响,
    • 在我们看到空字符之前,我们强烈希望复制计数的字符数而不是复制
    • 如果您声称要构建一个字符串,那么请确保您为 null 终止它

    如果 strlcpy() 在您的环境中可用,那么您可以使用它,否则为什么不编写自己的小实用函数呢?然后,如果您已本地化的那个函数中存在警告,那么问题就来了。

    【讨论】:

    • strncpynot intended for use with C strings。可以使用strlcpymemcpy,视情况而定。
    • 是的,strncpy 也是不安全的。 strlcpy 平台无关吗?我知道 Microsoft 的 sprintf_s 无法与其他编译器一起使用。
    【解决方案5】:

    在您的第一个示例中,您已经知道长度。由于您没有分配 length+1 字节,因此我假设 length 包括空终止符。在这种情况下,只需 std::copy 字符串:std::copy(extName, extName + length, expList->names[i]);

    在您的第二个示例中,假设源字符串为空终止,您可以计算目标字符串长度并再次使用std::copy 手动连接,或者您可以使用std::stringstd::copy 的结果中的c_str进入您的目的地(再次假设您为其分配了足够的空间)。

    c_str() 不分配需要外部删除的内存。

    最后请注意,sizeof(char) 将始终为 1,因此在您的 malloc 中是多余的,尽管该字符中的位数可能不是 8(请参阅CHAR_BIT)。

    【讨论】:

    • 使用 std::copy 也会给我一个关于它不安全的警告,因为它需要用户检查大小:(
    【解决方案6】:

    我认为你应该替换所有函数调用如果可能来调用你自己的实现。一个很好的例子是替换 strcpy 并在其中调用特定于编译器的 strcpy 版本的函数。然后可以轻松地修改您的实现以适应您选择的任何编译器,特别是如果您将添加或更改平台/编译器。

    例子:

    
    char* StringCopy(char* Destination, const char* Source, size_t DestinationSize)
    {
    #ifdef _MSC_VER
      return strcpy_s(Destination, Source, DestinationSize);
    #else
      if(!(strlen(Source) >= DestinationSize))
         return strcpy(Destination, Source);
      else
         return 0x0;
    #endif
    }
    

    【讨论】:

    • 抱歉!(strlen(Source) >= DestinationSize)。我无法让“
    • -1。标准实现没有未知错误,但您的新实现很可能会有它们。你将重新发明轮子。
    • 我不建议从头开始重写它们。不幸的是,根据您使用的平台/编译器,有几种标准实现的不同实现。我建议使用现有的函数,尽管确保你从函数中得到你想要的并不是负面的。一个很好的例子是 vsnprintf,它不会在 MSVC 上对中继字符串进行空终止,尽管 GCC 会。
    • 我现在看到,在我的文本中,我并没有明确表示您应该尽可能使用现有函数,尽管您应该将其包装在一个简化的 API 中。我的例子仍然显示了我真正的意思。
    • +1,好主意。尽管在非 MSVC 编译器中应该会更快崩溃。
    【解决方案7】:

    如果不关心可移植性,您可以使用'strcpy_s'

    【讨论】:

      【解决方案8】:

      如果这段代码只为windows平台编译,那么最好使用这些函数的安全版本。但是,如果此代码要跨多个平台(linux、Aix 等)编译,那么您可以使用 _CRT_SECURE_NO_WARNINGS 禁用 Windows 项目配置文件(例如 .vcxproj)中的警告,或者您可以使用代码 sn- p 在 .cpp 文件中调用这些函数的地方喜欢这个。

      #if _OS_ == _OS__WINDOWS
      //secure function call
      #else
      //already written code
      #endif
      

      【讨论】:

        【解决方案9】:

        按照消息中的建议,使用 _CRT_SECURE_NO_WARNINGS 禁用此警告。

        在ProjectProperties -> Configuration Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions中,添加如下宏:

        _CRT_SECURE_NO_WARNINGS

        【讨论】:

          猜你喜欢
          • 2023-03-06
          • 1970-01-01
          • 1970-01-01
          • 2015-11-16
          • 2015-03-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-18
          相关资源
          最近更新 更多