编译器警告(级别 3)C4996

你的代码使用标记为“已弃用”的函数、类成员、变量或 typedef。 符号是使用 __declspec(deprecated) 修饰符或 C++14 [[deprecated]] 属性弃用的。 实际的 C4996 警告消息由声明的 deprecated 修饰符或属性指定。

重要

此警告始终是声明符号的头文件的作者特意发出的消息。 在不了解后果的情况下,请不要使用已弃用的符号。

注解

Visual Studio 库中的许多函数、成员函数、函数模板和全局变量已 弃用。 某些函数/变量(例如 POSIX 和 Microsoft 特定的函数)被弃用的原因是它们现在使用不同的偏好名称。 某些 C 运行时库函数被弃用的原因是它们不安全,而现在具有更安全的变体。 其他函数/变量被弃用的原因是它们已过时。 弃用消息通常包括已弃用函数或全局变量的建议替换项。

/sdl(启用附加安全检查)编译器选项会将此警告升级为错误。

关闭警告

若要解决 C4996 问题,我们通常建议更改代码。 请改用建议的函数和全局变量。 如果出于可移植性原因而需要使用现有的函数或变量,可以关闭警告。

关闭针对特定代码行的警告

若要关闭针对特定代码行的警告,请使用 warning pragma #pragma warning(suppress : 4996)

在文件中关闭警告

若要在文件中针对随后的所有内容关闭警告,请使用警告 pragma #pragma warning(disable : 4996)

在命令行生成中关闭警告

若要在命令行生成中全局关闭警告,请使用 /wd4996 命令行选项。

在 Visual Studio 中关闭针对项目的警告

若要在 Visual Studio IDE 中关闭针对整个项目的警告,请执行以下操作:

  1. 打开项目的“属性页”对话框。 有关如何使用“属性页”对话框的信息,请参阅属性页

  2. 选择“配置属性”>“C/C++”>“高级”属性页。

  3. 编辑“禁用特定警告”属性以添加 4996。 选择“确定”以应用更改。

使用预处理器宏禁用警告

还可以使用预处理器宏来关闭库中使用的弃用警告的某些特定类。 下面介绍了这些宏。

若要在 Visual Studio 中定义预处理器宏,请执行以下操作:

  1. 打开项目的“属性页”对话框。 有关如何使用“属性页”对话框的信息,请参阅属性页

  2. 展开“配置属性”>“C/C++”>“预处理器”。

  3. 在“预处理器定义”属性中,添加宏名称。 选择“确定” 进行保存,然后重新生成项目。

若要仅在特定的源文件中定义宏,请在包含头文件的任何行之前添加一行(例如 #define EXAMPLE_MACRO_NAME)。

下面是 C4996 警告和错误的一些常见源代码:

POSIX 函数名称

The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name: new-name. See online help for details.

Microsoft 在 CRT 中重命名了一些 POSIX 和 Microsoft 特定的库函数,以符合 C99 和 C++03 在保留名称和全局实现定义的名称方面的约束。 只弃用了默认名称,函数本身未弃用。 在大多数情况下,函数名称中添加了一个前导下划线以创建符合标准的名称。 编译器会发出对原始函数名称的弃用警告,并建议偏好名称。

若要解决此问题,我们通常建议更改代码以改用建议的函数名称。 但是,更新后的名称特定于 Microsoft。 如果出于可移植性原因而需要使用现有的函数名称,可以关闭这些警告。 这些函数仍在库中以其原始名称提供。

若要关闭这些函数的弃用警告,请定义预处理器宏 _CRT_NONSTDC_NO_WARNINGS。 可以通过在命令行中包含选项 /D_CRT_NONSTDC_NO_WARNINGS 来定义此宏。

不安全的 CRT 库函数

This function or variable may be unsafe. Consider using safe-version instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

Microsoft 弃用了一些 CRT 和 C++ 标准库函数与全局变量,因为有更安全的版本可用。 大多数已弃用的函数允许在不经过检查的情况下对缓冲区进行读取或写入访问。 滥用这些函数可能导致严重的安全问题。 编译器会发出对这些函数的弃用警告,并建议首选函数。

若要解决此问题,我们建议改用函数或变量 safe-version。 有时,出于可移植性或后向兼容性原因,你不能这样做。 请仔细验证你的代码中是否不允许发生缓冲区覆盖或过度读取。 然后可以关闭警告。

若要在 CRT 中关闭这些函数的弃用警告,请定义 _CRT_SECURE_NO_WARNINGS

若要关闭有关弃用的全局变量的警告,请定义 _CRT_SECURE_NO_WARNINGS_GLOBALS

有关这些已弃用函数和全局变量的详细信息,请参阅 CRT 中的安全功能安全库:C++ 标准库

不安全的标准库函数

'std:: function_name ::_Unchecked_iterators::_Deprecate' Call to std:: function_name with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'

在 Visual Studio 2015 中,此警告显示在调试版本中,因为某些 C++ 标准库函数模板不会检查参数是否正确。 原因通常是函数没有足够的信息用于检查容器边界。 或者,错误地将迭代器与函数结合使用。 此警告可帮助你识别这些函数,因为它们可能是程序中严重安全漏洞的起因。 有关详细信息,请参阅检查的迭代器

例如,如果将元素指针而不是普通数组传递给 std::copy,则会在调试模式下出现此警告。 若要解决此问题,请使用正确声明的数组,以便库可以检查数组范围并执行边界检查。

// C4996_copyarray.cpp
// compile with: cl /c /W4 /D_DEBUG C4996_copyarray.cpp
#include <algorithm>

void example(char const * const src) {
    char dest[1234];
    char * pdest3 = dest + 3;
    std::copy(src, src + 42, pdest3); // C4996
    std::copy(src, src + 42, dest);   // OK, copy can tell that dest is 1234 elements
}

在 C++14 中,已将多个标准库算法更新为使用“双范围”版本。 如果使用双范围版本,则第二个范围将提供必要的边界检查:

// C4996_containers.cpp
// compile with: cl /c /W4 /D_DEBUG C4996_containers.cpp
#include <algorithm>

bool example(
    char const * const left,
    const size_t leftSize,
    char const * const right,
    const size_t rightSize)
{
    bool result = false;
    result = std::equal(left, left + leftSize, right); // C4996
    // To fix, try this form instead:
    // result = std::equal(left, left + leftSize, right, right + rightSize); // OK
    return result;
}

此示例演示了使用标准库来检查迭代器用法的其他多种方式,以及在哪种情况下不检查用法可能会带来风险:

// C4996_standard.cpp
// compile with: cl /EHsc /W4 /MDd C4996_standard.cpp
#include <algorithm>
#include <array>
#include <iostream>
#include <iterator>
#include <numeric>
#include <string>
#include <vector>

using namespace std;

template <typename C> void print(const string& s, const C& c) {
    cout << s;

    for (const auto& e : c) {
        cout << e << " ";
    }

    cout << endl;
}

int main()
{
    vector<int> v(16);
    iota(v.begin(), v.end(), 0);
    print("v: ", v);

    // OK: vector::iterator is checked in debug mode
    // (i.e. an overrun triggers a debug assertion)
    vector<int> v2(16);
    transform(v.begin(), v.end(), v2.begin(), [](int n) { return n * 2; });
    print("v2: ", v2);

    // OK: back_insert_iterator is marked as checked in debug mode
    // (i.e. an overrun is impossible)
    vector<int> v3;
    transform(v.begin(), v.end(), back_inserter(v3), [](int n) { return n * 3; });
    print("v3: ", v3);

    // OK: array::iterator is checked in debug mode
    // (i.e. an overrun triggers a debug assertion)
    array<int, 16> a4;
    transform(v.begin(), v.end(), a4.begin(), [](int n) { return n * 4; });
    print("a4: ", a4);

    // OK: Raw arrays are checked in debug mode
    // (i.e. an overrun triggers a debug assertion)
    // NOTE: This applies only when raw arrays are
    // given to C++ Standard Library algorithms!
    int a5[16];
    transform(v.begin(), v.end(), a5, [](int n) { return n * 5; });
    print("a5: ", a5);

    // WARNING C4996: Pointers cannot be checked in debug mode
    // (i.e. an overrun triggers undefined behavior)
    int a6[16];
    int * p6 = a6;
    transform(v.begin(), v.end(), p6, [](int n) { return n * 6; });
    print("a6: ", a6);

    // OK: stdext::checked_array_iterator is checked in debug mode
    // (i.e. an overrun triggers a debug assertion)
    int a7[16];
    int * p7 = a7;
    transform(v.begin(), v.end(),
        stdext::make_checked_array_iterator(p7, 16),
        [](int n) { return n * 7; });
    print("a7: ", a7);

    // WARNING SILENCED: stdext::unchecked_array_iterator
    // is marked as checked in debug mode, but it performs no checking,
    // so an overrun triggers undefined behavior
    int a8[16];
    int * p8 = a8;
    transform( v.begin(), v.end(),
        stdext::make_unchecked_array_iterator(p8),
        [](int n) { return n * 8; });
    print("a8: ", a8);
}

如果你已确认代码不能出现缓冲区溢出错误,则可以关闭此警告。 若要关闭针对这些函数的警告,请定义 _SCL_SECURE_NO_WARNINGS

已启用检查的迭代器

如果在 _ITERATOR_DEBUG_LEVEL 定义为 1 或 2 的情况下不使用检查的迭代器,则也可能会出现 C4996。 对于调试模式生成,此参数默认设置为 2;对于零售生成,此参数设置为 0。 有关详细信息,请参阅检查的迭代器

// C4996_checked.cpp
// compile with: /EHsc /W4 /MDd C4996_checked.cpp
#define _ITERATOR_DEBUG_LEVEL 2

#include <algorithm>
#include <iterator>

using namespace std;
using namespace stdext;

int main() {
    int a[] = { 1, 2, 3 };
    int b[] = { 10, 11, 12 };
    copy(a, a + 3, b + 1);   // C4996
    // try the following line instead:
    // copy(a, a + 3, checked_array_iterator<int *>(b, 3));   // OK
}

不安全的 MFC 或 ATL 代码

如果出于安全原因而使用已弃用的 MFC 或 ATL 函数,则可能会出现 C4996。

若要解决此问题,我们强烈建议更改代码以改用更新的函数。

有关如何抑制这些警告的信息,请参阅 _AFX_SECURE_NO_WARNINGS

过时的 CRT 函数和变量

This function or variable has been superseded by newer library or operating system functionality. Consider using new_item instead. See online help for details.

某些库函数和全局变量由于过时已弃用。 可能会在未来版本的库中删除这些函数和变量。 编译器会发出对这些项的弃用警告,并建议首选备用项。

若要解决此问题,我们建议更改代码以使用建议的函数或变量。

若要关闭针对这些项的弃用警告,请定义 _CRT_OBSOLETE_NO_WARNINGS。 有关详细信息,请参阅弃用的函数或变量的文档。

CLR 代码中的封送处理错误

使用 CLR 封送处理库时,也可能会出现 C4996。 在这种情况下,C4996 是错误而非警告。 使用 marshal_as 在需要 marshal_context的两种数据类型之间进行转换时,会发生此错误。 如果封送处理库不支持转换,则也可能会出现此错误。 有关封送处理库的详细信息,请参阅 C++ 中的封送处理概述

此示例生成 C4996,因为封送处理库需要上下文才能从 System::String 转换为 const char *

// C4996_Marshal.cpp
// compile with: /clr
// C4996 expected
#include <stdlib.h>
#include <string.h>
#include <msclr\marshal.h>

using namespace System;
using namespace msclr::interop;

int main() {
   String^ message = gcnew String("Test String to Marshal");
   const char* result;
   result = marshal_as<const char*>( message );
   return 0;
}

示例:用户定义的已弃用函数

不再建议你使用某些函数时,你可以在自己的代码中使用 deprecated 属性来警告调用方。 在此示例中,在两个位置生成了 C4996 警告:一个警告针对声明已弃用函数的行,另一个警告针对使用该函数的行。

// C4996.cpp
// compile with: /W3
// C4996 warning expected
#include <stdio.h>

// #pragma warning(disable : 4996)
void func1(void) {
   printf_s("\nIn func1");
}

[[deprecated]]
void func1(int) {
   printf_s("\nIn func2");
}

int main() {
   func1();
   func1(1);    // C4996
}