C语言:跨平台环境下使用snprintf,vsnprintf系列函数要注意返回值的问题
标准C语言函数snprintf,vsnprintf
系列函数可以向指定的缓冲区输出格式化打印的字符串。
如果指定的缓存区足够大,那么调用正常,返回值就是写入缓存区的字节长度(不含结尾'\0'
)
那么缓存区不够大的情况呢?
本文要说的是这系列函数的在缓存区长度不足以输出所有内容时的返回值在不同一编译器提供的实现表现是不同的。
我们用如下一段简单的测试代码来验证其返回值表现。
test_snprintf.c
#include <stdio.h>
#include <errno.h>
#include <string.h>int main()
{char buf[4] = { 0xff };int wsz = snprintf(buf,sizeof buf,"hello");printf("buf=%s,write size %d\n",buf,wsz);if(wsz < 0){printf("snprintf ERROR %d:%s\n", errno, strerror(errno));}else if(wsz >= sizeof buf ) {printf("snprintf buffer overflow\n");}/** 16进制输出缓冲区内容 */ for(int i = 0; i < sizeof buf; ++i){printf("%x,",buf[i]);}printf("\n");
}
上面代码中长度为4字节的buffer显然是无法完整输出hello,分别在MinGW(GCC 5.2.0)和MSVC(vs2015)
编译上面的代码。并运行它们,如下是运行结果。
因为输出的内容超过了buffer大小从运行结果看buf中的结果是不一致的,返回值也是不一样的。
在MSVC下返回的是待输出字符串('hello'
)的大小,而GCC下则是-1,
这不会吧?都是遵循C语言标准,为什么会出现不同的结果?
其实吧,标准这东西就是技术巨头们互相谈判妥协的结果。这两种不同的返回值都符合C语言标准,因为标准就是Microsoft这些巨头们制定的,当返回结果不一样又互不妥协的时候,那就把两种结果都写进标准吧。
下面是C语言标准库(C standard library)关于vsnprintf,snprintf函数的说明原文
https://en.cppreference.com/w/c/io/fprintf
https://en.cppreference.com/w/c/io/vfprintf
下面的截图红框标注的部分为snprintf
函数返回值定义:
翻译出来就是如果输入参数bufsz(缓冲区大小)为0,则返回应该写入buffer的长度(不含结尾’\0’),如果出错返回负值
下面的截图红框标注的部分为vsnprintf
函数返回值定义:
翻译出来就是如果成功返回写入buffer的字符数量,如果出错返回负值。如果因为buffer长度限制而输出结果被截断,则函数返回应该写入buffer的字符数量,前提是这个buffer长度限制不是强制的
(if the limit was not imposed
这一句我理解为如果buffer长度为0就是强制的 )。
把上面两段英文原文结合着看就差不多明白了。
GNU的实现的逻辑就是只要buffer长度不足,就认为是出错了,输出-1,然后把标准错误代码 errno
置为ERANGE(34)
(不会把buffer最后一字节设置为‘\0’结尾,这样无结尾的字符串很危险了)。
MSC的实现逻辑是,不管buffer长度是多少,都不认为是出错,调用者可以通过返回值是不是超过了buffer的大小来判断是否完整输出(不论怎样会把buffer最后一字节设置为‘\0’结尾)。
这两种逻辑都是遵循C语言标准库定义的。
所以前面的测试代码进一步可以如下完善就可以在跨平台使用场景中更加安全的判断输出缓冲区是否不足了:
#include <stdio.h>
#include <errno.h>
#include <string.h>int main()
{char buf[4] = { 0xff };int wsz = snprintf(buf,sizeof buf,"hello");printf("buf=%s,write size %d\n",buf,wsz);if(wsz < 0){/** GNU C 下输出内容超过缓冲区大小要通过写入长度是否小于0以及errno是否为ERANGE来判断 */if(errno == EREANGE){printf("snprintf buffer overflow\n");}else{/** 其他错误 */printf("snprintf ERROR %d:%s\n", errno, strerror(errno));}}else if(wsz >= sizeof buf ) {/** MSC 下输出内容超过缓冲区大小通过写入长度是否buffer长度来判断 */printf("snprintf buffer overflow\n");}
}
C语言:跨平台环境下使用snprintf,vsnprintf系列函数要注意返回值的问题相关推荐
- 在OpenCV环境下写的灰度图像二维傅里叶换,幅值计算,频谱平移和将数值归一化到0到255区间的四个函数
图像处理开发需求.图像处理接私活挣零花钱,请加微信/QQ 2487872782 图像处理开发资料.图像处理技术交流请加QQ群,群号 271891601 灰度图像的二维傅里叶变换(cv_gray_fft ...
- c语言子函数返回值,C语言函数说明与返回值
在学习C语言函数以前,我们需要了解什么是模块化程序设计方法. 人们在求解一个复杂问题时,通常采用的是逐步分解.分而治之的方法,也就是把一个大问题分解成若干个比较容易求解的小问题,然后分别求解.程序员在 ...
- C语言入门---函数类型与返回值(int和void)
int和void的区别? 哈喽,各位小伙伴们,在我们学习C语言的过程中经常会看到书中的main函数前带有int和void,就像下面这样: 上面这两种有什么区别呢?在C语言中,可以把函数分为有返回值函数 ...
- linux 函数返回string,linux 下c函数strcmp的返回值疑问?
linux 下c函数strcmp的返回值疑问? strcmp函数解释: NAME strcmp, strncmp - compare two strings SYNOPSIS #include int ...
- 计算机辅助培训的策略,宁波诺丁汉大学学习策略培训对解决计算机辅助语言教学环境下信息过剩问题的启示...
摘要: 信息技术和网络的不断发展在改变世界的同时也在改变人们的学习环境.计算机辅助语言教学(CALL)已普遍融入中国英语课堂内外.学生在课外上网,使用光盘或课件进行自主学习是课外自学很好的方式.CAL ...
- h5怎么区分在ios、安卓、微信环境下?怎么调用原生函数
第一步:建立dieu.js文件,创建全局变量DEVICE_TYPE //兼容页面字体 ;(function flexible (window, document) {function isMobile ...
- C语言main函数参数、返回值
C语言main函数返回值: main函数的返回值,用于说明程序的退出状态.如果返回0,则代表程序正常退出:返回其他数字的含义则由系统决定,通常,返回非零代表程序异常退出,即使程序运行结果正确也仍需修复 ...
- 【C语言程序】输出杜甫的《绝句》(有无返回值两种方法)
题出自-------------------------------零基础学C语言 1.没有返回值 #include<stdio.h> void jueju(); int main() { ...
- c语言自定义函数多个返回值,C语言函数返回值
C语言函数返回值教程 如果,我们希望函数不返回任何值,那么我们需要显式的指明其返回类型为 C语言函数不返回值 语法 void funcName(paramType1 param1, paramType ...
最新文章
- TVM优化c++部署实践
- 教程:7、管道和过滤器
- 为Spring Cloud Config Server配置远程git仓库
- JavaScript比较中应使用哪个等于运算符(== vs ===)?
- (转载)php array_merge 和 两数组相加区别
- 自然语言处理库——Gensim之Word2vec
- step4 . day1标准IO和文件IO
- 新课推荐 | 用 Django 快速搭建 API 测试工具
- Android之稍微靠谱点的透明Activity(不获取触摸事件)
- 网络延长器分为哪几类?其应用领域有哪些?
- 心情随笔(三):注入新的血液
- 从DB-Engines看传统数据库生存状况
- mysql时间加8小时_劳斯莱斯库里南,超精致1:8模型车,组装时间长达450个小时...
- NVIDIA显卡驱动重装
- python爬虫:批量下载qq空间里的照片(二)
- 智工教育:一消《技术实务》知识点整理
- WPF—TimeLine类
- airtest上的滑动操作swipe
- 在uniapp的小程序中使用自己的字体库
- c语言程序 计算离高考天数,用c++程序计算一个孩子从出生到高考需要多少天