fopen被弃用警告

74

使用 Visual Studio 2005 C++ 编译器 编译代码中使用 fopen() 等函数时,我会收到以下警告:

1>foo.cpp(5) : warning C4996: 'fopen' was declared deprecated
1>        c:\program files\microsoft visual studio 8\vc\include\stdio.h(234) : see declaration of 'fopen'
1>        Message: 'This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_DEPRECATE. See online help for details.'

我该如何防止这种情况发生?

11个回答

139

看起来微软已经废弃了许多使用缓冲区来提高代码安全性的调用。然而,他们提供的解决方案并不可移植。无论如何,如果您不想使用他们安全版本的调用(如fopen_s),则需要在包含的头文件之前放置_CRT_SECURE_NO_DEPRECATE的定义。例如:

似乎微软已经废弃了许多使用缓冲区来提高代码安全性的调用。然而,他们提供的解决方案并不可移植。无论如何,如果您不想使用他们带有安全保障的调用(如 fopen_s),则需要在包含的头文件之前放置_CRT_SECURE_NO_DEPRECATE的定义。例如:

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>

预处理器指令也可以添加到项目设置中,以便在项目下的所有文件中使用。要执行此操作,请将_CRT_SECURE_NO_DEPRECATE 添加到 项目属性 -> 配置属性 -> C / C ++ -> 预处理器 -> 预处理器定义


11
您可能需要像这样做: #ifdef _WIN32 #define _CRT_SECURE_NO_DEPRECATE #endif #include <stdio.h>因为其他平台在编译时不需要定义它。 - markwatson
1
@markwatson 更好的保护措施是检查 #ifdef _MSC_VER - MicroVirus
我正在使用Visual Studio 2019,将´#define _CRT_SECURE_NO_DEPRECATE´添加到我的代码中并没有起作用。我不得不进入项目设置,在C/C++ --> 预处理器 --> 预处理器定义下手动在字符串末尾添加“;_CRT_SECURE_NO_DEPRECATE”。 - ilmu011

31

你可以添加一个:

#pragma warning (disable : 4996)

在使用fopen之前,您是否考虑过按照警告建议使用fopen_s?它会返回一个错误代码,让您可以检查函数调用的结果。

只是禁用弃用函数警告的问题在于,微软可能会在CRT的后续版本中删除相关的函数,从而破坏您的代码(如下面的评论所述,在这种情况下不会发生这种情况,因为fopen是C和C++ ISO标准的一部分)。


41
如果Microsoft不再希望实现C或C ++标准,他们可能会在CRT的后续版本中删除相关功能。 - Steve Jessop
5
有些人也会攻击非 MS 平台。而且使用这些 _s 函数,并不能真正带来显著的安全提升。 - sstn
17
对于未来搜索该问题的读者,"deprecated" 在此上下文中仅表示不建议使用该函数,而不表示该函数即将从CRT中移除。-msdn - Navin
1
@SteveJessop他们已经故意和自愿违反了标准。请参见此链接 - ApproachingDarknessFish
6
在使用VS2013时,我需要使用#pragma warning(disable:4996),因为建议使用的_CRT_SECURE_NO_WARNINGS_CRT_SECURE_NO_DEPRECATE都无法正常工作。这些#define在其他情况下似乎是有效的,所以请注意这似乎实现不一致。 - Bill Weinman
显示剩余2条评论

13

这只是微软的恶作剧。"Deprecated" 意味着某个语言特性可能在未来的标准语言/标准库版本中不被提供,由标准委员会规定。这并不意味着或者不应该意味着"我们单方面认为你不应该使用它",无论这个建议有多么好。


15
英语单词“deprecate”的意思准确来说是第二个意思:“我们认为你不应该使用它”。但在计算机行话中,它最近出现了一个更弱的含义,“可能不明智使用它,因为我们正在考虑将其移除,并且我们提供了我们认为更好的替代品”。 - Steve Jessop

10

如果您的代码是针对不同的操作系统(如Mac OS X、Linux)编写的,您可以使用以下方法:

#ifdef _WIN32
#define _CRT_SECURE_NO_DEPRECATE
#endif

7
我正在使用VisualStudio 2008。 在这种情况下,我经常设置预处理器定义

菜单 \ 项目 \ [项目名称]属性... Alt+F7

如果在项目窗口中点击此菜单或按下Alt + F7,您可以看到"属性页"窗口。

然后查看窗口左侧的菜单。

配置属性 \ C / C ++ \ 预处理器

然后将_CRT_SECURE_NO_WARNINGS添加到\预处理器定义中。


请先查看 如何回答问题这个问题已经在以前得到了回答,显然,你可以在这里添加你的回答。但是,在回答之前,你需要理解一些要点。首先,不要添加与以前相同的代码或建议的答案。其次,如果用户非常明确地询问了问题及所需解决的问题,请不要添加过于复杂的答案。第三,如果你想就答案或问题提出任何建议,可以添加评论。 - always-a-learner
这是我在Visual Studio 2017中唯一有效的答案。#define会留下很多C4996错误。(@ankitsuthar,你的评论对我来说似乎错了。这个答案简洁明了,它起作用,并且与其他答案在重要细节上有所不同。) - Bob Stein

4
考虑使用可移植性库,比如glibapache portable runtime。这些库通常提供安全、可移植的替代方案来代替这些调用。这也是一个好事情,因为这些不安全的调用在大多数现代环境中已经被弃用了。

1
Microsoft的fopen_s()函数是一种非标准的、不太兼容的、不可移植的实现,而其便携式替代方案则是C标准函数fopen() - Andrew Henle

1
许多微软的安全函数,包括fopen_s(),都是C11的一部分,因此它们现在应该是可移植的。您应该意识到安全函数在异常行为和有时返回值方面有所不同。此外,您需要知道,尽管这些函数已经标准化,但它们是标准的可选部分(附录K),至少glibc(在Linux上默认)和FreeBSD的libc没有实现。

然而,我为此问题奋斗了几年。我发布了一个更大的转换宏集here.。对于您的即时问题,请将以下代码放入一个包含文件中,并将其包含在您的源代码中:

#pragma once
#if !defined(FCN_S_MACROS_H)
   #define   FCN_S_MACROS_H

   #include <cstdio>
   #include <string> // Need this for _stricmp
   using namespace std;

   // _MSC_VER = 1400 is MSVC 2005. _MSC_VER = 1600 (MSVC 2010) was the current
   // value when I wrote (some of) these macros.

   #if (defined(_MSC_VER) && (_MSC_VER >= 1400) )

      inline extern
      FILE*   fcnSMacro_fopen_s(char *fname, char *mode)
      {  FILE *fptr;
         fopen_s(&fptr, fname, mode);
         return fptr;
      }
      #define fopen(fname, mode)            fcnSMacro_fopen_s((fname), (mode))

   #else
      #define fopen_s(fp, fmt, mode)        *(fp)=fopen( (fmt), (mode))

   #endif //_MSC_VER

#endif // FCN_S_MACROS_H

当然,这种方法没有实现预期的异常行为。

1
MS的_s与C11的边界检查接口通常不同。有些具有相同的签名,而有些则没有。由于该接口是可选的,因此很少有实现支持它,因为它大多数情况下都是不必要的。而且你的代码是C++,而不是你在文本中提到的C。 - too honest for this site
@Olaf:嗯,我无法解决边界检查问题,除了指出我的异常行为警告。 - riderBill
你说得对,我引用了C11标准(而不是C++14或17)。这些函数在最近版本的微软C++编译器中可用。结果发现,除了MSVS之外,Annex K并没有得到广泛支持。Jonathan Leffler在这里发表评论(https://dev59.com/nkfRa4cB1Zd3GeqP-JF9#35193374),指出MS版本实际上并不符合Annex K规范。所以,可移植性就这样打水漂了。 - riderBill
1
如果您能完整地阅读我的评论,我将不胜感激。MSVC实际上并没有使用BCI,而是使用了他们自己的一套工具。至少在过去18年中,MSVC一直没有遵守标准,并且非常明确地表示不打算这样做,至少包括C99强制要求的特性,例如VLA,这些特性已经被每个现代编译器支持多年(包括主要嵌入式编译器)。 - too honest for this site
没有人表现出无礼。我认为这样的介绍相当侮辱人。“Microsoft的许多安全函数,包括fopen_s(),都是C11的一部分,所以它们现在应该是可移植的”,“你说得对,我引用了C11标准”——因此我们可以放心地期望您了解这个标准,否则您为什么要这样说呢?至于其他方面:谷歌一下就行了。所有帖子都可以编辑,评论5分钟。那应该足够了。也许你不应该跳过[导览]?供参考:http://port70.net/~nsz/c/c11/n1570.html 我把它放在我的枕头下(有关此讨论,请参见附录K) - too honest for this site
显示剩余3条评论

1
如果您希望它在许多平台上使用,您可以像注释中所说使用定义,例如:
#if defined(_MSC_VER) || defined(WIN32)  || defined(_WIN32) || defined(__WIN32__) \
                        || defined(WIN64)    || defined(_WIN64) || defined(__WIN64__) 

        errno_t err = fopen_s(&stream,name, "w");

#endif

#if defined(unix)        || defined(__unix)      || defined(__unix__) \
                        || defined(linux)       || defined(__linux)     || defined(__linux__) \
                        || defined(sun)         || defined(__sun) \
                        || defined(BSD)         || defined(__OpenBSD__) || defined(__NetBSD__) \
                        || defined(__FreeBSD__) || defined __DragonFly__ \
                        || defined(sgi)         || defined(__sgi) \
                        || defined(__MACOSX__)  || defined(__APPLE__) \
                        || defined(__CYGWIN__) 

        stream = fopen(name, "w");

#endif

你的复杂 #IF 只针对平台,而不是编译器版本。那么 #if (defined(_MSC_VER) && (_MSC_VER >= 1600) ) ... #ELSE ... 怎么样?这应该涵盖所有情况,对吧? - riderBill
_MSC_VER = 1600 可能不是第一个弃用 fopen() 等函数的版本。但这是我遇到该问题的第一个版本。 - riderBill
看起来第一个带有(一些)安全函数的 MSVC 版本是 2005 年,_MSC_VER = 1400。 - riderBill

1

对于使用Visual Studio 2017版本的用户,似乎运行不安全操作所需的预处理器定义已更改。请改用:

#define _CRT_SECURE_NO_WARNINGS

它将会编译。


1
@LeviRoberts 你必须把这个定义放在文件的最顶部,当一些头文件被包含时会生成错误。 - jrh

0

我也遇到了同样的问题。当我尝试添加OpenCV库时。

#include <opencv\cv.h>

我收到的不是警告,而是错误。

error C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.    c:\program files (x86)\opencv\build\include\opencv2\flann\logger.h  

我也像提到的那样使用了预处理指令,但这并没有解决问题。

我通过以下方式解决了这个问题:

  • 转到属性 -> C/C++ -> 预编译头 -> 在预编译头中选择不使用预编译头。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接