您的位置:首页 > 运维架构

fopen,fopen_s,fclose, _fcloseall函数用法

2016-07-01 00:00 525 查看
fopen函数的使用

在定义FILE * fp 之后,fopen的用法是: fp = fopen(filename,"w")。而对于fopen_s来说,还得定义另外一个变量errno_t err,然后err = fopen_s(&fp,filename,"w"),注意第一个参数是指向FILE*的指针,即二重指针。返回值的话,对于fopen来说,打开文件成功的话返回文件指针(赋值给fp),打开失败则返回NULL值;对于fopen_s来说,打开文件成功返回0,失败返回非0。

在vs编程中,经常会有这样的警告:warning 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. 是因为 fopen_s比fopen多了溢出检测(question:什么地方用了溢出检测,这里也没有像gets_s???),更安全一些。(在以后的文章里还有get与get_s的比较,strcpy strcpy_s的比较,他们的共同点都是用来一些不可预料的行为,以后将进行详尽解释)

Closes a stream (fclose) or closes all open streams (_fcloseall).

int fclose( FILE *stream );

int _fcloseall( void );

Function Required Header Compatibility

fclose
<stdio.h>
ANSI, Win 95, Win NT

_fcloseall
<stdio.h>
ANSI, Win 95, Win NT

For additional compatibility information, see Compatibility in the Introduction.

Libraries

LIBC.LIB
Single thread static library, retail version

LIBCMT.LIB
Multithread static library, retail version

MSVCRT.LIB
Import library for MSVCRT.DLL, retail version

Return Value

fclose returns 0 if the stream is successfully closed. _fcloseall returns the total number of streams closed. Both functions return EOF to indicate an error.

Parameter

stream

Pointer to FILE structure

Remarks

The fclose function closes stream. _fcloseall closes all open streams except stdin, stdout, stderr (and, in MS-DOS®, _stdaux and _stdprn). It also closes and deletes any temporary files created by tmpfile. In both functions, all buffers associated with the stream are flushed prior to closing(note:所有的与这些流相关联的缓冲区都在这些流被关闭前被刷新,从而保证不会因为关闭而丢失数据). System-allocated buffers are released when the stream is closed. Buffers assigned by the user with setbuf and setvbuf are not automatically released.(ASK:setbuf和setvbuf这两个函数有什么作用呢?)

Example

/* FOPEN.C: This program opens files named "data"
* and "data2".It  uses fclose to close "data" and
* _fcloseall to close all remaining files.
*/
#include <stdio.h>
FILE *stream, *stream2;
int main(void)
{
int numclosed;
errno_t err;
// Open for read (will fail if file "crt_fopen_s.c" does not exist)
if ((err = fopen_s(&stream, "crt_fopen_s.c", "r")) != 0)
printf("The file 'crt_fopen_s.c' was not opened\n");
else
printf("The file 'crt_fopen_s.c' was opened\n");
// Open for write
if ((err = fopen_s(&stream2, "data2", "w+")) != 0)
printf("The file 'data2' was not opened\n");
else
printf("The file 'data2' was opened\n");
// Close stream if it is not NULL
if (stream)
{
if (fclose(stream))
{
printf("The file 'crt_fopen_s.c' was not closed\n");
}
}
// All other files are closed:
numclosed = _fcloseall();
printf("Number of files closed by _fcloseall: %u\n", numclosed);
}[/code]
Output





================

_s类函数的使用策略

首先介绍下gets_s、getws_s函数的用法。gets_s, _getws_s https://msdn.microsoft.com/en-us/library/5b5x9wc7.aspx
Gets a line from the stdin stream. These versions of gets, _getws have security enhancements(安全加强), as described in Security Features in the CRT.

char *gets_s(
char *buffer,
size_t sizeInCharacters
);
wchar_t *_getws_s(
wchar_t *buffer,
size_t sizeInCharacters
);
template <size_t size>
char *gets_s(
char (&buffer)[size]
); // C++ only
template <size_t size>
wchar_t *_getws_s(
wchar_t (&buffer)[size]
); // C++ only

Parameters

[out]
buffer

Storage location for input string.

[in]
sizeInCharacters

The size of the buffer.

Return Value

Returns buffer if successful. A NULL pointer indicates an error or end-of-file condition. Use ferror or feof to determine which one has occurred.

Remarks

The gets_s function reads a line from the standard input stream stdin and stores it in buffer. The line consists of all characters up to and including the first newline character ('\n'). gets_s then replaces the newline character with a null character ('\0') before returning the line. In contrast, the fgets_s function retains the newline character.

If the first character read is the end-of-file character, a null character is stored at the beginning of buffer and NULL is returned.

_getws is a wide-character version of gets_s; its argument and return value are wide-character strings.

If buffer is NULL or sizeInCharacters is less than or equal to zero, or if the buffer is too small to contain the input line and null terminator, these functions invoke an invalid parameter handler, as described in Parameter Validation. If execution is allowed to continue, these functions return NULL and set errno to ERANGE.

In C++, using these functions is simplified by template overloads; the overloads can infer buffer length automatically (eliminating the need to specify a size argument) and they can automatically replace older, non-secure functions with their newer, secure counterparts. For more information, see Secure Template Overloads.

顺便说一下,scanf与scanf_s、gets与gets_s(note:这里的gets_s里的第一个s表示string即字符串,第二个表示safe)、以及这里的fopen_s和fopen,之所以说带_s的函数安全,并不是说在在用户输入超过缓冲区长度时不报错,而是正好相反,在检测到缓冲区不够时,一定会报错。

From Security Features in the CRT https://msdn.microsoft.com/en-us/library/8ef0s5kh.aspx

The secure functions do not prevent or correct security errors; rather(而是), they catch errors when they occur. They perform additional checks for error conditions, and in the case of an error, they invoke an error handler (see Parameter Validation).

不会像scanf接收字符到一个char数组里,如果用户输入的字符数超过了缓冲区的长度时,scanf也可能“一声不吭”(注:在一些编译器下,确实会存在这种情况),这种做法在一段时间内可能不会产生什么异常,但是操作不属于自己的内存空间,这本身就是一个漏洞,即所谓的“缓冲区溢出漏洞”,经常被一些黑客利用。想想如果这些被误操作的字节,正好对应一个关键数据,或者是对应一个函数的入口地址(注:这种情况下,黑客可以利用缓冲区溢出漏洞将这个入口地址改为它自己编写的木马程序的入口地址。你就悲剧了!纯粹道听途说,自己没试过!!)

》》》可能有些同学对gets_s在接收到超过缓冲区长度的数据时的弹出错误或警告窗体的做法表示不解,其实我的心里是想让按下面方式行事。

一、当用户输入的数据的长度(注:到底指什么要搞清楚)不超过缓冲区长度时,正常接收换行符前的所有字符。

(note:如果不注意的话,这里又是一个pitfall,当然前提是你在使用gets_s时,输入中既有中文,又有英文等西方字符,后面写一篇关于接收单字节字符和宽字符的区别的日志。对于实用程序应该始终使用w_char)。

二、当用户的输入的数据的长度超过缓冲区长度时,接收“第二个参数的值减一”个字符,并将缓冲区最后一个字符设置为终止符。(ASK:宽字符中的终止符是什么样的?两个字节的全0吗?)。

对于我们的第二条愿望,标准库中的默认实现一般会是报错提醒,但是真正的实用的应用程序,用户不会允许自己使用的软件动不动就报错,暂且不讨论有多少用户会使用从控制台接收输入的软件。实际上这也是可以实现的。

From Parameter Validation https://msdn.microsoft.com/en-us/library/ksazx244.aspx

Most of the security-enhanced CRT(C运行时) functions and many of the preexisting(先前存在的) functions validate their parameters. This could include checking pointers for NULL, checking that integers fall into a valid range, or checking that enumeration values are valid. When an invalid parameter is found, the invalid parameter handler is executed.
note:注意这时提到一些验证参数的例子,翻译如下:
一、指针是否为空。
二、整型是否会落入一个有效的范围。
三、检查枚举类型是否有效。
而且如果一个无效的参数被发现,会执行相应的无效参数处理机制。???是每一种类型的参数异常对应一种处理机制吗?

Invalid Parameter Handler Routine
--------------------------------------------------------------------------------
The behavior of the C Runtime when an invalid parameter is found is to call the currently assigned invalid parameter handler. The default(note:这里应该指的是运行时,而不是调试时) invalid parameter invokes Watson crash reporting, which causes the application to crash and asks the user if they want to load the crash dump to Microsoft for analysis. In Debug mode, an invalid parameter also results in a failed assertion.如下图所示,是gets_s函数接收的用户输入超过缓冲区长度时的警告窗口。





This behavior can be changed by using the function _set_invalid_parameter_handler to set the invalid parameter handler to your own function.(note:你可以通过set_invalid_parameter_handler 函数来设定无效参数 的处理机制到你自己定义的函数) If the function you specify does not terminate the application, control is returned to the function that received the invalid parameters, and these functions will normally cease execution, return an error code(note:这个收到无效参数的函数会正常地结束执行,并返回一个错误码。注意反省一下,自己在处理相应的问题时,对于非法输入是都能返回相应的错误码。ASK:在编程时如何利用函数返回值和errno值????), and set errno to an error code. In many cases, the errno value and the return value are both EINVAL, indicating an invalid parameter. In some cases, a more specific error code is returned, such as EBADF for a bad file pointer passed in as a parameter. For more information on errno, see errno, _doserrno, _sys_errlist, and _sys_nerr.
==========
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: