【问题标题】:C++ 'strcpy' gives a Warning (C4996)C++ 'strcpy' 给出警告 (C4996)
【发布时间】:2010-10-25 06:21:01
【问题描述】:

我收到此警告,但所有功能都正常工作。

这究竟意味着什么?

'strcpy': This function or variable may be unsafe. 
Consider using strcpy_s instead. To disable deprecation, 
use _CRT_SECURE_NO_WARNINGS. See online help for details.

【问题讨论】:

  • @GMan:编译器不会发出“只是为了好玩”的警告。这些警告是有原因的,忽略它们通常是一个坏主意。如果编译器已经告诉你一个问题,你应该认真听它。在这种情况下,它是关于潜在安全问题(或只是潜在的崩溃源)的警告,并要求您改用更健全的功能。
  • @DarkDust:除了我永远不会(!)使用strcpy(或strcpy_s)这一事实之外,使用普通的C函数非常好,并且使用“安全” CRT 变体只给人一种安全的错觉,仅此而已。更重要的是,它们不便携。
  • @DarkDust:我相信这种警告对于通常不太关心字符串边界的初学者或懒惰的程序员很有用。如果你确定你有一个以 NULL 结尾的字符串(不是来自用户输入),我不明白为什么我们应该避免使用完美工作的函数。
  • @ereOn:许多 C 程序员(例如 Torvalds)倾向于避免使用所有 C 字符串函数,因为它们是不必要且低效的:改用 memcpy
  • @DarkDust:哦,我很清楚许多警告很重要。但并非全部都是,这肯定是一个该死的烦人警告。有时,不管你信不信,程序员以完全安全的方式使用这些函数。 @Gabe:因为 CERT 以某种方式定义了标准……?

标签: c++ visual-c++


【解决方案1】:

这个函数 (strcpy) 被认为是不安全的,因为它没有边界检查并且可能导致缓冲区溢出。 (实际上 strcpy 因溢出漏洞而臭名昭著,所有程序员都避免使用它,或者至少应该避免使用它)。建议是使用考虑目标缓冲区大小的安全函数以避免溢出。您也可以使用 strncpy (但要小心!)。您的代码没有问题,即函数将按照您的说法运行,但尝试提供一个大于目标缓冲区的缓冲区作为输入。该函数将溢出目标缓冲区。也检查一下link text

【讨论】:

  • 好吧,所有程序员都避免使用它来处理用户输入而不进行检查。但是在你确定你的字符串是 NULL 终止的上下文中,你可以安全地使用它。
  • @ereOn:问题是标准C函数不知道目标缓冲区的大小。即使输入的src缓冲区正确地以NULL终止,如果目标缓冲区没有办法停止复制缓冲区的大小较小。这必须由调用函数处理,或者必须使用 strcpy 函数的安全版本。你是对的,处理用户输入是一个大问题。但总的来说,最好不要在所有情况下一起使用 strcpy。
  • 不要使用 strncpy();它不保证以空值结尾的字符串,如果用于将短字符串复制到大缓冲区中,它会热心地将缓冲区中未使用的部分清零,这很少真正需要。它的原始工作很好(将最多 14 个字符复制到旧 Unix 文件系统中的目录条目中),但对于其他工作则不然。
  • @user384706: memcpy 不知道 NUL,但我是程序员。如果我要为strcpy 编写一个更安全的替代品,那么我将在每个项目中最多执行一次(可能会更少,因为在大多数C++ 项目中你可以只使用std::string),而且我可以负担得起注意缓冲区长度和 NUL 终止,并正确处理。使用strncpy 编写的任何此类函数最好使用memcpy 编写,或者可能根本不调用库,除非由最初没有商业编程C++ 的人编写,他会弄乱memcpy 版本。
  • 我猜我的意思是,strcpy 在大量使用时是“不安全的”,因为最终你会犯错。在经过良好测试的辅助函数中每个项目使用一次并不是“不安全的”。或者如果是,那么作者不需要 CERT 建议告诉他不要使用 strcpy,他需要 CERT 建议告诉他不要使用 C 或 C++。
【解决方案2】:

虽然 strcpy 是一个常见的字符串函数,但它一直是软件中许多错误和安全漏洞的根源(由于缓冲区容易溢出)。

为了促进 C 和 C++ 中更安全的编码,Microsoft 为危险的字符串方法提供了一套替换函数。通常,它们的原始名称后缀为 _s。因此,Microsoft 安全版本的 strcpy 是警告中建议的 strcpy_s。请注意,这是 Microsoft 特有的功能,并非无处不在。

您有几个选择。

  1. 如果您不想关心它,请定义 _CRT_SECURE_NO_WARNINGS,从而在您的软件中留下安全问题的可能性。
  2. 将字符串函数替换为安全函数,从而降低软件的可移植性
  3. 包装安全字符串函数并在任何地方使用包装器,在 Windows 平台上提供增强的安全性,并在其他平台上回退到传统版本。包装函数可以通过宏或编译函数。

我通常做#3。

【讨论】:

  • 我敢肯定还是有可能犯错,但是你有没有想到一个具体的例子?
  • 您是否查看了 James McNellis 在评论中发布的链接?
  • 哦。我的眼睛今天不工作了。好的,所以某些版本的 Visual Studio 中存在错误。
【解决方案3】:

由于您正在编程 C++,因此正确的解决方案是尽可能在您的代码中禁止 C 风格的 char* 字符串,并用 std::string(或其他适当的字符串类型)替换它们。

不要不要使用strcpystrcpy_sstrncpy等函数。使用string 类的复制构造函数或赋值运算符。或者,如果您确实需要复制缓冲区,请使用std::copy

【讨论】:

    【解决方案4】:

    从 VC++ 8 strcpy() 和大量其他函数 are considered to be unsafe 开始,因为它们没有边界检查,如果滥用可能导致缓冲区溢出。

    你有两个选择:

    • 如果您不确定 - 按照 VC++ 所说的去做并使用“安全”函数。它们将触发一个错误处理程序,如果出现问题,该处理程序将终止您的程序。
    • 如果您知道自己在做什么 - 您知道永远不会发生溢出并且所有边缘情况都由您的代码处理 - 在包含 CRT 标头之前定义 _CRT_SECURE_NO_WARNINGS,这将使警告消失。李>

    【讨论】:

    • 如果您决定使用“安全”功能,您可能会喜欢我在博客中提到的 VS 2010 扩展,它会在您构建之前为您标记这些功能,并在工具提示中为您提供替代品。 gregcons.com/KateBlog/BannedAPIsFlaggedForYou.aspx
    【解决方案5】:

    实际上有一种方法可以避免这个警告,仍然使用 strcpy,并且是安全的:

    您可以启用secure template overloads。他们将(如果可能)通过使用模板化重载捕获缓冲区来推断使用的缓冲区的长度。为什么在 Visual C++ 中默认不启用此功能对我来说是个谜。

    【讨论】:

      【解决方案6】:

      该警告基本上是在告诉您 strcpy 已被弃用,因为将字符串复制到 \0 很容易导致严重的问题(缓冲区溢出)。 strcpy 仍然存在并且有效的原因是它是标准库遗留的一部分,但您应该真正考虑使用 str*_s 或 strn* 函数(它们不完全依赖于找到终止 \0)。

      由于缓冲区溢出不仅与安全问题有关,还与相对难以跟踪和修复的错误有关,因此使用普通的 str* 函数不仅通常不受欢迎,而且可能导致人们拒绝您的代码不安全。

      更多详情: http://www.safercode.com/blog/2008/11/04/unsafe-functions-in-c-and-their-safer-replacements-strings-part-i.html

      【讨论】:

      • Vladski:您的链接明确指出您推荐的 strn* 函数容易出现问题。
      【解决方案7】:
      #pragma warning(disable: 4996)
      

      在代码的第一行使用上述代码。

      【讨论】:

      • 这不回答问题,它只是抑制警告
      【解决方案8】:

      如果您已经了解了使用 C++ 纯粹技术的优缺点,而不必担心,因为您“知道”您的字符串将以零结尾,那么您也可以禁用 msvc 中的警告,这类事情:

      #ifdef _MSC_VER
        // 4231: nonstandard extension used : 'extern' before template explicit instantiation
        // 4250: dominance
        // 4251: member needs to have dll-interface
        // 4275: base needs to have dll-interface
        // 4660: explicitly instantiating a class that's already implicitly instantiated
        // 4661: no suitable definition provided for explicit template instantiation request
        // 4786: identifer was truncated in debug information
        // 4355: 'this' : used in base member initializer list
        // 4910: '__declspec(dllexport)' and 'extern' are incompatible on an explicit instantiation
      #   pragma warning(disable: 4231 4250 4251 4275 4660 4661 4786 4355 4910)
      #endif
      

      【讨论】:

        【解决方案9】:
        #pragma once
        #define _CRT_SECURE_NO_WARNINGS 1 
        #define _WINSOCK_DEPRECATED_NO_WARNINGS 1 
        

        文件顶部对我有用 (基于其他 SO 用户的回答......但我找不到参考他/她)

        【讨论】:

          【解决方案10】:

          使用Secure Template Overloadsdefine wrapper functions 不适用于动态分配的缓冲区,因此这种尝试是徒劳的。
          要么修改源以使用安全替换,要么直接忽略它。

          如果代码是自己写的,最好把这样的strcpy改成strcpy_s等。 如果模块是从受信任的来源导入的,您可以选择忽略警告。

          忽略方法1:项目全局范围:添加_CRT_SECURE_NO_WARNINGS
          忽略方法2:忽略特定模块:如果只有其中一个或两个,那么您可以简单地在包含这些模块时禁止警告:

          #pragma warning(push)
          #pragma warning(disable: 4996)
          #include <sapi.h>  //legacy module
          #include <sphelper.h> //legacy module
          #pragma warning(pop)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-03-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-08-10
            • 1970-01-01
            相关资源
            最近更新 更多