文档章节

【整理】snprintf辨析

摩云飞
 摩云飞
发布于 2013/12/12 09:54
字数 1406
阅读 283
收藏 0
点赞 0
评论 0

      长久以来,在大多数 C 实现上,snprintf 都是作为一个非标准的扩展存在的。随着 C99 标准的颁布,snprintf 终于浮上台面而成为合法功能,目前 snprintf 已经是 C99 标准中的正式一员。不过,除非你的编译器是符合 C99 标准的,否则可能仍然必须使用供应商提供的非标准扩展,如 _snprintf 。

      坦白地说,早该使用 snprintf 来取代 sprintf 了,即使在 snprintf 还没有标准化之前。大多数良好的编码标准都不推荐你使用像 sprintf 这样的不检查长度的函数,而且该原则是很有道理的。使用不做检查的 sprintf 长久以来会引起一些声名狼藉的常见问题,它通常会导致程序崩溃,尤其会导致安全脆弱问题。

借助于 snprintf,我们就可以正确编写刚才一直试图实现的带长度检查的 PrettyFormat() 版本。
// 示例 3-1:在 C 中使用 snprintf 来字符串化某些数据
//
void PrettyFormat(int i, char* buf, int buflen) {
    // 这就是代码,简洁优雅,关键是比以前要安全得多:
    snprintf(buf, buflen, "%4d", i);
}

       注意,即便这样做了,仍然还存在另一种出错的可能,即调用者将缓冲区长度搞错了。这意味着跟那些具有资源管理功能的替代方案相比,snprintf 还算不上百分之百地杜绝缓冲区溢出可能性,不过跟 sprintf 相比它显然要安全多了,在“长度是否安全?”这个问题上应该算是合格的。使用 sprintf 没有合适的途径来绝对避免缓冲区溢出,而通过 snprintf,我们则可以(很大程度上)杜绝缓冲区溢出。

      注意,snprintf 的一些标准化之前版本的行为稍有不同。尤其是在一个主要实现中,如果输出结果填满或者大于缓冲区容量,缓冲区里的串就不会以 '\0' 结尾。这种情况下,我们的 PrettyFormat() 函数就得稍作调整以应付这种非标准的行为:
// 在C中使用一个并不十分遵从C99标准的_snprintf来将数据字符串化
//
void PrettyFormat(int i, char* buf, int buflen) {
    // 这里是代码,简洁优雅,而且安全得多
    if(buflen > 0) {
        _snprintf(buf, buflen-1, "%4d", i);
        buf[buflen-1] = '\0';
    }
}

      C++11,先前被称作 C++0x,即 ISO/IEC 14882:2011,是目前的 C++ 编程语言的正式标准。它取代第二版标准 ISO/IEC 14882:2003(第一版 ISO/IEC 14882:1998 公开于 1998 年,第二版于 2003 年更新,分别通称C++98 以及 C++03,两者差异很小)。

参考文章:
1.《sprintf_s与_snprintf与_snprintf_s》   
2.《snprintf函数使用(Windows与Linux版本)》  
3.《snprintf、stringstream、strstream以及boost::lexical_cast的对比分析》  
4. 网页


      vs2010 中没有 snprintf 函数,但提供了 _snprintf,因为 snprintf 是 c99 的一部分,微软没有支持 c99,转而支持 c++11,而 _snprintf 是 c++11 的一部分。

The  glibc  implementation of the functions snprintf() and vsnprintf() conforms to the C99 standard
在 glibc 实现中支持的 snprintf() 和 vsnprintf() 均符合 C99 标准。

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

linux 下 glibc 实现了符合 C99 标准的 snprintf(...) 。
int snprintf(char *str, size_t size, const char *format, ...);

windows 下 VS2010 实现了符合 C++11 标准的 _snprintf(...)。
int _snprintf(char *buffer, size_t count, const char *format[, argument]...);

最常见的错误用法有:
1.
char sa[256]={0};
_snprintf(sa,sizeof(sa),"%s",sb);

错误原因:当 sb 的长度 >= 256 的时候,sa 将没有 '\0' 结尾。

2.
char sa[256];
_snprintf(sa,sizeof(sa)-1,"%s",sb);

错误原因:当 sb 的长度 >= 255 的时候,sa 将没有 '\0' 结尾,忘记给 sa 初始化。

3.
char sa[256];
_snprintf(sa,sizeof(sa)-1,"%s",sb);
sa[sizeof(sa)]=0;

错误原因:最后一行数组越界。

正确的用法:

1. //推荐用法
char sa[256];
sa[sizeof(sa)-1]=0;
_snprintf(sa,sizeof(sa),"%s",sb);
if(sa[sizeof(sa)-1]!=0)
{
   printf("warning:string will be truncated");
   sa[sizeof(sa)-1]=0;
}

2.

char sa[256]={0};
int result = _snprintf(sa,sizeof(sa),"%s",sb);
if(result==sizeof(sa) || result<0)
{
    printf("warning:sting will be truncated");
   sa[sizeof(sa)-1]=0;
}

个人第二种方法较好!

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

      snprintf 函数并不是标准 c/c++ 中规定的函数,但是在许多编译器中,厂商提供了其实现的版本。在 gcc 中实现称为 snprintf,而在 VC 中实现为 _snprintf。由于不是标准函数,故没有一个统一的标准来规定该函数的行为,所以导致了各厂商间的实现版本可能会有差异。

函数定义为:
int _snprintf( char *buffer, size_t count, const char *format [, argument]... );

差异就发生在 count 参数。

在 VC 中,参数 count 是要写入的字符串的总字符数。
#include <stdio.h>
#include <string.h>
int main()
{
    char str[5];
    memset(str,0,sizeof(str));
    int rt = _snprintf(str,3,"%s","abcdefg");
    printf("%d\n",rt);
    printf("%s",str);
    return 0;
}

vc 程序的输出是:

-1
abc

在 Gcc 中,参数 count 是要向 buff 中写入 3 个字符,包括 '\0' 字符。

#include <stdio.h>
#include <string.h>
int main()
{
    char str[5];
    memset(str,0,sizeof(str));
    int rt = snprintf(str,3,"%s","abcdefg");
    printf("%d\n",rt);
    printf("%s",str);
    return 0;
}

gcc 程序的输出是:

7
ab

从输出结果可以知道:

  • VC 中的 _snprintf 的 count 参数表示,会向 buff 中写入 count 个字符,不包括 '\0' 字符,并且不会在字符串末尾添加 '\0' 符。而且字符串长度超过参数 count 时,函数返回 -1,以表示可能导致错误;
  • gcc 中的 snprintf 函数的 count 参数表示,向 buff 中写入 count 个字符,包括 '\0' 字符,并且返回实际的字符串长度。

© 著作权归作者所有

共有 人打赏支持
摩云飞
粉丝 367
博文 534
码字总数 952690
作品 0
徐汇
程序员
linux c/c++ 面试题目整理(一)

1、求下面函数的返回值 问:假定x是9999,那么返回多少? 答:返回的是8,解题思路是将x转化为二进制,看含有多少个1,则就返回多少。 2、文件中有一组整数,要求排序后输出到另一个文件中 ...

晟夏的叶
2017/04/19
0
0
辨析三层架构MVC与MVP

辨析三层架构MVC与MVP 请前辈辨析~ thanks http://my.oschina.net/barter/blog/90855

JavaOlder
2013/01/21
295
0
snprintf和sprintf区别分析

今天在项目中使用snprintf时遇到一个比较迷惑的问题,追根溯源了一下,在此对sprintf和snprintf进行一下对比分析。 因为sprintf可能导致缓冲区溢出问题而不被推荐使用,所以在项目中我一直优...

水海云
2013/11/18
0
1
acl 网络通信服务器框架 3.0.22 版本发布

acl 3.0.22 版本发布了,acl 是 one advanced C/C++ library 的简称,主要包括网络通信库以及服务器框架库等功能,支持 Linux/Windows/Solaris/FreeBsd/MacOS 平台;整个 acl 项目主要包含三...

郑树新
2014/12/16
2.5K
9
关于在vs2010下,snprintf的使用须知

#include<stdio.h> #include<stdlib.h> void f(const char *p) { char buf[4]={}; _snprintf(buf, sizeof(buf), "%6s", p); printf("%c",buf); printf("%2s",buf); } int main() { char s[4]......

开源小牛
2014/07/14
0
0
snprintf()函数返回值

函数原型: int snprintf(char str, size_t size, const char format, ...); size 的作用就是限制往str写入不超过size个字节(包括了结尾的'0')。 因为sprintf()函数如果成功的话,返回成功写...

呼噜呼噜睡翻天
2012/12/14
0
0
snprintf()和sprintf()格式化字符串

int snprintf(char str, size_t size, const char format, ...); 将可变个参数(...)按照format格式化成字符串,然后将其复制到str中 (1) 如果格式化后的字符串长度 < size,则将此字符串全部...

晨曦之光
2012/04/13
847
0
【C语言】取值符&和间接值*辨析

计算机程序在存储数据时必须跟踪三种基本属性: 数据是什么 信息在存储器中的 存储的是多少 众所周知,对于常规变量a,是存储地址,a是值。对于指针变量p,p是存储地址,则是值。下面通过一个...

realsa
2016/04/12
71
0
sprintf和snprintf的正确使用

关于sprintf和snprintf的正确使用考虑以下有缺陷的例子: void f(const char *p) { char buf[11]={0}; sprintf(buf,"%10s",p); // very dangerous printf("%sn",buf); } 不要让格式标记“%10......

陈文东
2012/09/03
0
0
linux Gcc 4.4.7 snprintf莫名崩溃

#if 0 ///!!!!BUGS linux -O3这下这个函数直接崩溃原因不明 int appendLen = snprintf(U.rpt.datas, sizeof(U)-URLRPTMIN_LEN, "%s", (*iter)->url); #else int appendLen = sprintf(U.rpt.......

little_kid
2014/09/24
604
4

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Webpack使用nodemon实时打包编译

业务场景: 1.编写一个npm组件包并且link到了项目文件中 2.需要不断的修改并run build编译npm包并且在项目run dev 查看效果 3.问题: 每次改完npm包都要手动run build编译十分的麻烦且低效,可不...

JamesView
12分钟前
0
0
电脑炸了,浪费我好几天时间,还是简要记下来吧

我的小本本一直在兢兢业业的干活,然而前几天说炸就炸了...... 爆炸现场: 软件: windows10 pro + EIS11+ 360卫士 BIOS:N1DET98W 2.24 硬件: Xeon E3 1505-V5 nv-M3000M thinkpadP70:20E...

Oh_really
16分钟前
0
0
Git之branch和checkout

1.branch是查看、创建、删除分支 #>git branch --helpNAME git-branch - List, create, or delete branchesSYNOPSIS git branch [--color[=<when>] | --no-color] [......

汉斯-冯-拉特
18分钟前
0
0
Mybatis拦截器之数据权限过滤与分页集成

需求场景 最近项目有个数据权限的业务需求,要求大致为每个单位只能查看本级单位及下属单位的数据,例如:一个集团军下属十二个旅,那么军级用户可以看到所有数据,而每个旅则只能看到本旅部...

佛系程序猿灬
27分钟前
8
0
SpringCloud 微服务 (十六) 服务追踪 Zipkin

问题 在服务中,有一个接口,该A接口中又调用了其他服务的B、C、D接口,出现一个请求耗时大的问题,这时候并不知道该B、C、D接口中哪个接口造成的耗时量,然后比如确定C服务接口出现的耗时量大,但...

___大侠
今天
0
0
Java面试基础篇——第八篇:抽象类与接口的区别

1.抽象类 抽象类:如果一个类中包含有抽象方法,或这个类使用abstract关键字修饰,则称这个类是抽象类。 抽象方法是什么呢?抽象方法就是指用abstract关键字修饰的方法。 需要注意的是:抽象...

developlee的潇洒人生
今天
2
0
jsoup 相关资料

1.jsoup 2.Jsoup概述 3.jsoup入门 4.jsoup Java HTML Parser 1.11.3 API

IT追寻者
今天
1
0
JPA @MappedSuperclass 注解说明

基于代码复用和模型分离的思想,在项目开发中使用JPA的@MappedSuperclass注解将实体类的多个属性分别封装到不同的非实体类中。 1.@MappedSuperclass注解只能标准在类上:@Target({java.lang....

海博1600
今天
0
0
【一】Scala Configuration 相关API

Play使用了 Typesafe config library,但是也提供了一个有着更多Scala高级特性的的 Configuration 封装。不熟悉Typesafe配置的开发者可以移步 configuration文件的语法和特性文档。 读取配置...

Landas
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部