fopen / fopen_s 和文件写入

25

我在C语言中使用fopen将输出写入文本文件。函数声明如下(其中ARRAY_SIZE已经被定义):

void create_out_file(char file_name[],long double *z1){  
  FILE *out;  
  int i;  

  if((out = fopen(file_name, "w+")) == NULL){  
    fprintf(stderr, "***> Open error on output file %s", file_name);  
    exit(-1);  
  }  

  for(i = 0; i < ARRAY_SIZE; i++)  
    fprintf(out, "%.16Le\n", z1[i]);  
  fclose(out);  
}  

我的问题:

  1. 在使用MVS2008进行编译时,我收到了警告:警告C4996:“fopen”:此函数或变量可能不安全。建议改用fopen_s。我没有看到有关fopen_s的太多信息,以便更改我的代码。 有什么建议吗?

  2. 能否指示fprintf以所需的数字精度将数字写入文件? 如果我使用long double,那么我认为我的答案在小数点后15位之前是正确的。 我对吗?


17
fopen() 函数本身并不会有任何安全问题,但微软公司的一些人似乎对于以空字符结尾的字符串作为参数的函数失去了理智。 - anon
5
我认为这一切都是微软的阴谋,旨在让人们只能使用Windows编程。 - lost_in_the_source
6个回答

22

fopen_sfopen的一个变种,它包含参数验证,在打开过程中发生错误时返回错误代码而不是指针。相比基本变种,它更加安全,因为考虑了更多边缘条件。编译器警告你使用它,因为fopen在应用程序中可能存在潜在的利用漏洞。

你可以使用格式说明符%.xg来指定printf函数族的精度,其中x是你想要在输出中显示的精度位数。long double的精度因平台而异,但通常至少具有16个小数精度位。

编辑:虽然我并不完全赞同其他人声称fopen_s是完全浪费时间的观点,但它确实代表了一种相当低的利用风险,并且得到支持的范围也不广泛。然而,在C4996下发出警告的其他一些函数则是更严重的漏洞,使用_CRT_SECURE_NO_WARNINGS等价于关闭了“您将卧室门锁上了”和“您在厨房放置了一枚核弹”的警报。

只要你不受限于使用“纯C”进行项目(例如学校作业或嵌入式微控制器),你最好利用现代C编译器几乎都是C++编译器的事实,并使用这些I/O函数的C++ iostream变种,以便同时获得改进的安全性和兼容性。


谢谢。我的代码中有 fprintf(out, "%.16Le\n", z1[i]),是的,我在小数点后得到了16位精度数字。 - yCalleecharan
再次感谢。我使用C语言,因为我更熟悉它。我正在编写一段用于研究目的的代码,需要给出准确的结果。我必须将方程式进行无量纲化并形成C代码,以获得一个良好的值范围,从而防止过大或过小的值进入模拟和解决方案。我对C++不是很了解,也不知道是否可以安全地将我的文件保存为cpp并使用C ++ iostream而不会导致其他部分的代码编译错误。现在,我会仅限于使用C语言,直到我知道如何切换到C ++。 - yCalleecharan
使用 _CRT_SECURE_NO_WARNINGS 相当于同时关闭了“你把卧室门锁没锁”和“你在厨房放了一颗核弹”的警报。说得好! - user140327

12

我在使用 Visual Studio 2012 时遇到了类似的问题,但我的问题扩展开来是因为我正在构建一个程序,我希望能够利用 Visual Studio 的测试功能,并最终能够编译和在我的 Linux 服务器上运行相同的应用程序(我正在制作一个机器人)。

所以这就是我在谷歌搜索后得出的结论,我想发布它以帮助其他人。

FILE *fp_config;
const char *configfile ;
configfile = "bot.conf";
#ifdef WIN32
    errno_t err;
    if( (err  = fopen_s( &fp_config, configfile, "r" )) !=0 ) {
#else
    if ((fp_config = fopen(configfile, "r")) == NULL) {
#endif
        fprintf(stderr, "Cannot open config file %s!\n", configfile);
    }

这将使Visual Studio平静,不会抱怨,同时还允许相同的代码在gcc或任何符合标准的C / C ++编译器上编译


9
  1. fopen_s和其他所有的_s函数都是微软特有的“安全”变种标准函数。如果您的代码不需要跨平台,您可以切换并使编译器高兴。否则,只需将_CRT_SECURE_NO_WARNINGS预处理器指令添加到您的项目设置中,它就不会再警告您了。

  2. 是的,long double很容易达到15位精度;实际上,即使是普通的double也足够了(但不能超过这个值)。


谢谢。在我的32位机器上,双精度和长双精度都给我16位数字的精度,第16位数字是不准确的。 - yCalleecharan
是的,“long double”的大小取决于平台;可惜的是,MSVC++将其设置为与“double”相同,因此没有实际用途。64位双精度在尾数中具有53位精度,大约为15.95个十进制数字(log10(53))。 - tzaman
fopen_s和其他_s变体的引入是为了提供更高的安全性,这些在ISO C11标准中有定义。Visual Studio可能有点过早行动,但允许选择C11(和C17)的编译器应该提供这些功能。 - orcmid

5

从fopen到fopen_s的转换禁用了在打开并写入文件时以只读方式在记事本中打开文件的功能。切换回fopen后,我可以在程序写入文件的同时阅读文件。


3
参考文献中可以了解到,在使用fopen_s或freopen_s时,对于任何用"w"或"a"创建的文件,文件访问权限会防止其他用户访问它。文件访问模式标志"u"可以选择地添加到以"w"或"a"开头的任何说明符中,以启用默认的fopen权限。(C11) - Brady Dean

4
其他网友指出,fopen并不是非常不安全的函数。如果你不想看到这个警告,但又想看到其他真正存在漏洞的警告,请不要使用#define _CRT_SECURE_NO_WARNINGS
相反,下次当你看到fopen的警告时,点击“查看'fopen'的声明”这一行。这将带你到stdio.h中注入警告的那一行。从该行中删除_CRT_INSECURE_DEPRECATE(fopen_s),你就不会在使用fopen时再收到安全警告,但对于strcpy、strdup和其他可能危险的函数仍然会有警告。

1
实际上,在Windows上,“标准”的open实现由于那些年仅14、125、16岁的淘气鬼们无聊地寻找黑客程序并破坏他人计算机而存在一些漏洞,因此他们想出了一种不同的传递文件描述符的方法,使得这些青少年更难攻击。 - BrierMay

0

只需在包含任何文件之前定义_CRT_SECURE_NO_WARNINGS即可摆脱此警告,并停止相信 MS 关于fopen的说法。


谢谢。只需要在顶部添加 #define _CRT_SECURE_NO_WARNINGS 吗? - yCalleecharan

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