文件和流操作相关基础函数

1、fgets函数char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。

  • str -- 这是指向一个字符数组的指针,该数组存储了要读取的字符串。
  • n -- 这是要读取的最大字符数(包括最后的空字符)。通常是使用以 str 传递的数组长度。
  • stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了要从中读取字符的流。

如果成功,该函数返回相同的 str 参数。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。如果发生错误,返回一个空指针。
实例:if( fgets (str, 60, fp)!=NULL ) 

2、

sprintf(Linux/Windows)

Linux下的函数原型:int sprintf(char *str, const char *format, ...);

在VS2017环境中,这个函数被标记为不安全的,如果使用了,编译器会报警告,如果非要使用,必须在编译的时候增加宏定义:_CRT_SECURE_NO_WARNINGS,告诉编译器忽略安全警告。在Linux下此函数可以正常使用。而且这个函数在Windows下和Linux下行为也是一样的。具体如下:

 sprintf(data.buf, "%d", 12); 

1.当源数据的长度【小于】len,sprintf把数据完整的写到目标内存,并保证尾部以0结尾,返回写入的字节数。此时该函数的行为是安全的。

2.当源数据的长度【等于】len,sprintf把数据完整的写到目标内存,并在目标内存的尾部多写入一个0,返回写入的字节数。此时该函数已经发生拷贝越界的情况了。所以,当用户以为分配的内存刚刚好满足拷贝需求的时候,其实已经发生了潜在的风险。

3.当源数据的长度【大于】len,sprintf把数据完整的写到目标内存,返回写入的字节数,压根不管内存越界的情况,甚至连个错误码都不返回。

总结:以上三组实验结果,在Windows和Linux下均可以得到验证,可见sprintf函数的安全系数几乎为0,不推荐大家使用。
vsprintf的行为与sprintf一样。

wsprintf(LPTSTR lpOut,  LPCTSTR lpFmt, ….// 其他可选参数 );wsprintf函数能够将一组字符序列按lpFmt参数指定的格式转换,然后保存在lpOut参数指定的字符缓冲区中等待输出。如果要用wprintf输出Unicode的字符串,需要在wsprintf的第二个参数前面加L。

sprintf_s(Windows only)

为了弥补sprintf函数的不足,高版本的MSVC环境中引入了sprintf_s函数,在调用的时候支持用户传入目标内存的长度,函数原型可以简略的表示为:

 int sprintf_s(char *buf, size_t buf_size, const char *format, ...); 

例如:

 sprintf_s(data.buf, len, "%d", 12); 

1.当源数据的长度【小于】len,sprintf把数据完整的写到目标内存,并保证尾部以0结尾,返回写入的字节数。此时该函数的行为是安全的。

2.当源数据的长度【等于】或者【大于】len的时候,调用此函数将会触发断言。Debug模式下会弹出运行时错误提示框,告诉用户"Buffer too small";Release模式下程序会直接崩溃。

Debug模式下执行,会触发assert,如下图:

总结:sprintf_s函数只能在Windows下使用,虽然不会出现写坏内存的情况,但是会触发assert,导致程序中断,使用起来也要慎重。
vsprintf_s的行为与sprintf_s一样。

_snprintf(Windows only)

也许是觉得sprintf_s也不够安全,MSVC环境中还引入了一个名为_snprintf的函数,其函数原型和sprintf_s类似,可以表示为:

 int _snprintf(char *buf, size_t buf_size, const char *format, ...); 

 _snprintf(data.buf, len, "%d", 12); 

1、当源数据的长度【小于】len,能保证完整写入,并以0结尾,返回实际写入的字节数:

2、当源数据的长度【等于】len,能保证完整写入,结尾不做任何处理,返回实际写入的字节数:

3,当源数据的长度【大于】len,最多写入【len】个字符,结尾不错任何处理,返回【-1】:

总结:_snprintf函数只能在Windows下使用,最多写入【size】个字符,永远不破坏内存,也不会触发中断,但不能保证目标内存以0结尾。通过返回值可以知道函数调用是否成功,返回值>=0的时候,表示调用成功,返回了实际写入的字符数;返回值为-1的时候,表示目标内存太小,导致调用失败,但是已经尽力做了填充。

_vsnprintf的行为与_snprintf一样。

snprintf(Linux/Windows)

Linux下的函数原型为:

 int snprintf(char *str, size_t size, const char *format, ...); 

这个函数在Windows和Linux下均可以使用,并且行为一致。即:最多写入【size-1】个字符到目标内存,并保证以0结尾。返回值是【应该写入的字节数】,而不是【实际写入的字节数】

 snprintf(data.buf, len, "%d", 12); 
1、当源数据的长度【小于】len,能保证完整写入,并以0结尾,返回实际写入的字节数:

2、当源数据的长度【等于】len,实际上只写入了【len-1】个字符,最后一个字符用0填充,但返回值却是【len】:

3、当源数据的长度【大于】len,最多也只写入【len-1】个字符,最后一个字符用0填充,但返回值却是【应该要写入的字节数】:

总结:snprintf函数,可以在Linux/Windows双平台下使用,最多写入【size-1】个字符,永远不会破坏内存,也不会触发中断,并总能保证目标内存能以0结尾。唯一的问题是返回值不可靠,无法推断调用是否失败。

vsnprintf的行为与snprintf一样。

写到这里,sprintf系列的相关函数都讲完了,貌似没有一个完美的函数。不过既然知道了它们的具体行为,就可以根据应用场景挑选适合的函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值