编译器警告(级别 3) C4996

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

重要

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

注解

Visual Studio 库中的许多函数、成员函数、函数模板和全局变量都 已弃用。 某些函数(如 POSIX 和特定于 Microsoft 的函数)已弃用,因为它们现在具有不同的首选名称。 某些 C 运行时库函数已弃用,因为它们不安全,并且具有更安全的变体。 其他已弃用,因为它们已过时。 弃用消息通常包括建议的替换已弃用的函数或全局变量。

/sdl “启用其他安全检查”编译器选项会将此警告提升为错误。

关闭警告

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

关闭特定代码行的警告

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

关闭文件中的警告

若要关闭以下所有内容的文件中的警告,请使用警告杂注 #pragma warning(disable : 4996)

关闭命令行内部版本中的警告

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

在 Visual Studio 中关闭项目的警告

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

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

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

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

使用预处理器宏禁用警告

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

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

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

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

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

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

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

POSIX 函数名称

此项目的 POSIX 名称已弃用。 请改用 ISO C 和C++一致性名称: new-name。 有关详细信息,请参阅联机帮助。

Microsoft在 CRT 中重命名了一些 POSIX 和特定于 Microsoft 的库函数,以符合对保留和全局实现定义名称C++03 约束。 仅弃用名称,而不是函数本身。 在大多数情况下,将前导下划线添加到函数名称,以创建一个符合性的名称。 编译器针对原始函数名称发出弃用警告,并建议首选名称。

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

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

不安全的 CRT 库函数

此函数或变量可能不安全。 请考虑改用 safe-version 。 若要禁用弃用,请使用_CRT_SECURE_NO_WARNINGS。 有关详细信息,请参阅联机帮助。

Microsoft弃用了一些 CRT 和C++标准库函数和全局,因为提供了更安全的版本。 大多数已弃用的函数都允许对缓冲区进行未检查的读取或写入访问。 他们的滥用可能导致严重的安全问题。 编译器针对这些函数发出弃用警告,并建议首选函数。

若要解决此问题,建议改用函数或变量 safe-version 。 有时,出于可移植性或向后兼容性原因,无法实现。 请仔细验证缓冲区覆盖或过度写入代码中是否不可能发生。 然后,可以关闭警告。

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

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

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

不安全的标准库函数

“std:: :: function_name _Unchecked_iterators::_Deprecate” 调用 std:: function_name 具有可能不安全的参数 - 此调用依赖于调用方来检查传递的值是否正确。 若要禁用此警告,请使用 -D_SCL_SECURE_NO_WARNINGS。 请参阅有关如何使用 Visual C++“已检查迭代器”的文档

在 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

已启用检查的迭代器

如果未在定义为 1 或 2 时 _ITERATOR_DEBUG_LEVEL 使用检查迭代器,则也会发生 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 函数和变量

此函数或变量已被较新的库或作系统功能取代。 请考虑改用 new_item 。 有关详细信息,请参阅联机帮助。

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

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

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

在 CLR 代码中封送错误

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

此示例生成 C4996,因为封送处理库需要上下文从 a System::String 转换为 a 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
}