先放结论

1.在支持snpritf的编译器 ,只使用int snprintf( char *buffer, size_t count, const char *format [, argument] ... );

无论成功或者失败,都会返回字符串的总长度(不包括结束符),如果buffer的大小不足以存放,那么就会截断。buffer里格式化的字符串带结束符。

2.在旧版本的Visual Studio例如vs2005, 不支持snprintf函数。

使用int _snprintf_s( char *buffer, size_t sizeOfBuffer, size_t count, const char *format [, argument] ... );

用法: sizeOfBuffer参数永远填"缓冲区的大小-1", count参数永远填_TRUNCATE, 

char buf[4];
int sp_ret = _snprintf_s(buf, sizeof(buf) - 1, _TRUNCATE, "1234%d", 5);

成功则返回字符串长度, 如果buffer大小不足以存放字符串,则返回-1,字符串会截断保存在buffer里,带字符串结束符。

上面例子返回-1, buf里的数据是"123\0"。

总结

1.sprintf,参数不带缓冲区长度, 成功返回字符串长度, 格式化的字符串带结束符, 编译器都支持。 越界了程序不会立刻崩溃,难以发现。

2.snprintf,  参数带缓冲区长度,成功返回字符串长度, 格式化的字符串带结束符, 缓冲区不足时截断字符串,同时返回格式化字符串需要的buffer长度(不包括结束符)。 新版本的Visual Studio才支持,老版本无法使用。 安全。

3.sprintf_s, 参数带缓冲区长度,成功返回字符串长度,缓冲区不足时直接抛异常,程序崩溃。 新旧版本的Visual Studio都支持。 release版不建议使用,因为一旦缓冲区不足,异常没处理好,程序就挂了。

4._snprintf与snprintf差不多,唯一区别就是当字符串不算结束符的长度刚好跟缓冲区一样长的时候, 也会成功并把字符串写到缓冲区,这时候缓冲区是不带结束符的,建议用snprintf代替,避免后面的代码误以为字符串带结束符导致出错或者后面代码每次都要检查长度逻辑反而复杂化。

5.微软的一系列函数中,函数开头带_表示微软在c/c++标准以外的实现, _s的表示安全加强版本,参数错误例如缓冲区长度不足程序会抛异常,带w表示宽字符版本, 带_l的表示带locale_t参数的版本。

例如strcpy是标准里的函数, strcpy_s是安全加强版本, 虽然在c++11以后才有,但是老版本的Visual Studio就支持这个函数, 同样sprintf_s是sprintf对应的安全版本。 在VS2015以后,不带安全版本的字符串函数都废弃(deprecated)了, 如果使用了,编译器会报警。

例如sprintf处理char字符串, 宽字符版本的字符串wchar_t就用swprintf, 同样地_snprintf对应_snwprintf, _snprintf_s对应_snwprintf_s

例如atof对应的多一个locale_t参数的函数是_atof_l,  _snprintf_s对应_snprintf_s_l, _snwprintf_s对应_snwprintf_s_l。

6.sprintf以及snprintf都是跨平台可用。其他的我测试只在Windows平台可用。另外snprintf属于C99的内容,在Visual Studio老版本不能使用,只能用_snprintf等代替。

反正使劲并且只用snprintf就对了。除非在Windows平台并且用老古董的Visual Studio。另外strcpy和strcat等也换成strncpy, strncat(C99以后支持,并且不支持C99的VS老版本也支持)


1. int sprintf( char *buffer, const char *format [, argument] ... );

如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。

例子A:缓冲区足够放下整个字符串

char buf[4];
int sp_ret;
sp_ret = sprintf(buf, "12%d", 3);

成功, 返回3, 并且buf里的内容是"123\0" 一共4个字节。

例子B:缓冲区不足

char buf[4];
int sp_ret;
sp_ret = sprintf(buf, "123%d", 4);

返回值为4,内存越界,就是当时不死,也是迟早要完。


2. int snprintf( char *buffer, size_t count, const char *format [, argument] ... );

比sprinf多一个count参数,标识缓冲区的长度,避免越界,更加安全。

例子A:缓冲区足够放下整个字符串

char buf[4];
int sp_ret;
sp_ret = snprintf(buf, sizeof(buf), "12%d", 3);

成功,  返回3,  buf数据"123\0" 一共4个字节。 跟sprintf的表现差不多。

例子B:缓冲区不足

char buf[4];
int sp_ret;
sp_ret = snprintf(buf, sizeof(buf), "123%d", 4);

成功,返回4, buf数据"123\0" 一共4个字节。 目标字符串会被截断成buf刚好能放下的字符串(包括字符串结束符null terminator)。

char buf[4];
int sp_ret;
sp_ret = snprintf(buf, sizeof(buf), "1234%d", 5);

成功,返回5, buf数据"123\0" 一共4个字节。 目标字符串会被截断成buf刚好能放下的字符串(包括字符串结束符null terminator)。


3. int sprintf_s( char *buffer, size_t sizeOfBuffer, const char *format, ... );

跟sprintf对应,后面_s 代表 security, 是微软CRT函数针对sprintf函数的安全增强版,早在Vs2005版本就可以使用, 新版本的Visual Studio应该是2015以后,对sprintf函数标识为废弃,编译器就会提示警告,并且建议使用安全版本。 sprintf_s相比sprintf函数多了个sizeOfBuffer参数, 指定buffer的长度,避免越界。

例子A:缓冲区充足

char buf[4];
int sp_ret;
sp_ret = sprintf_s(buf, sizeof(buf), "12%d", 3);

成功,  返回3,  buf数据"123\0" 一共4个字节。

例子B:缓冲区不足(字符串结束符也要算)

char buf[4];
int sp_ret;
sp_ret = sprintf_s(buf, sizeof(buf), "123%d", 4);

这时候,程序直接抛异常。。。说好的更加安全,原来是抛异常尽早发现问题,就是如此的暴力,且枯燥。 曾经有个项目,在客户环境跑60多天就会崩溃一次,最后发现是因为有个定时记录日志的地方,buffer缓冲区设置比较小, 随时时间推移,计数越来越大,终于60多天超过缓冲区导致进程崩了。就因为一个无关紧要的功能用了sprintf_s函数,导致整个程序崩溃, 同时崩溃的还有我弱小的心灵。 从此我再也不敢用_s的函数了。 很多时候,字符串格式化截断,还是一种可以接受的结果,例如发现日志,或者某个显示的字符串不完整,至少客户还能正常使用,程序员也勉强能看出缓冲区不足的问题,尽快修复。 因此我比较倾向于使用snprintf代替。

同理, strcpy_s, strcat_s等,都比原来的strcpy, strcat函数多了一个缓冲区长度参数,也是出错直接抛异常。建议使用strncpy, strncat代替。

template <size_t size>

int sprintf_s( char (&buffer)[size], const char *format, ... ); // C++ only

这是sprintf_s在c++的另外一个重载版本, char数组,通过模板获取到缓冲区的大小,可以少填长度参数。 跟原版功能是一样的。


4. int _snprintf( char *buffer, size_t count, const char *format [, argument] ... );

snprintf前面的多下划线‘_‘’前缀, 在低版本Visual Studio(例如vs2005)不支持C99,  微软提供了一系列的'_'开头的函数, _snprintf, _strncpy, _strncat等。 用于对字符串缓冲区操作时,指定长度防止越界。 sprintf不指定缓冲区长度实在不太安全。 到了高版本的Visual Studio, 这些_开头的函数依旧可以使用。C99标准增加了strncpy, strncat等, 如果c++标准里有的函数,尽量用标准的。

例子A:缓冲区充足

char buf[4];
int sp_ret;
sp_ret = _snprintf(buf, sizeof(buf), "12%d", 3);

成功,  返回3,  buf数据"123\0" 一共4个字节。 跟snprintf的表现一致。

例子B:缓冲区刚好放下字符串,但不包括字符串结束符。

char buf[4];
int sp_ret;
sp_ret = _snprintf(buf, sizeof(buf), "123%d", 4);

成功,返回4,  buf数据"1234" 一共4个字节。 没有字符串结束符。

例子C:缓冲区连不包括结束符的字符串都放不下。

char buf[4];
int sp_ret;
sp_ret = _snprintf(buf, sizeof(buf), "1234%d", 5);

失败,返回-1。 buf数据"1234" 一共4个字节。没有结束符的字符串还是格式化到buf了。

在高版本的Visual Studio因为支持c++标准的std::snprintf, 建议直接使用std::snprintf代替。

在低版本的Visual Studio,在使用_snprintf的时候,为了确保字符串像std::snprintf一样截断。需要以下面方式使用。

char buf[4];
int sp_ret;
sp_ret = _snprintf(buf, sizeof(buf) - 1, "1234%d", 5);
if (sp_ret == -1) { // buf不足,截断buf[sizeof(buf) - 1] = '\0';
}

传入的sizeof(buf) - 1确保至少保留一个字节用于字符串结束符。

当返回-1的时候,表示buf前面的sizeof(buf) - 1都已填充满数据,这时候最后一个字节是没有结束符的,需要手动添加。

以上方式使用起来还是比较麻烦,低版本VS建议用_snprintf_s来代替

char buf[4];
int sp_ret;
sp_ret = _snprintf_s(buf, sizeof(buf) - 1, sizeof(buf) - 1, "1234%d", 5);

或者

char buf[4];
int sp_ret;
sp_ret = _snprintf_s(buf, sizeof(buf) - 1, _TRUNCATE, "1234%d", 5);

5.int _snprintf_s( char *buffer, size_t sizeOfBuffer, size_t count, const char *format [, argument] ... );

这个函数可以理解为 _snprintf + _s, 也就是_snprintf函数一模一样的功能, _s 意味着,安全,也意味着超过buffer长度就会崩溃。该函数vs2005就能用。

count是指字符串的最大长度。

当count参数等于sizeOfBuffer时,_snprintf_s的功能跟sprintf_s一模一样, 要么正常,要么崩溃。

当count参数等于sizeOfBuffer - 1或者 _TRUNCATE时,_snprintf_s跟snprintf的功能一模一样,两个函数成功都是返回字符串的长度,失败时_snprintf_s返回-1, 而snprintf返回字符串需要的总长度。#define _TRUNCATE ((size_t)-1), _TRUNCATE是一个特殊标识,告诉该函数如果字符串超过范围则截断。

当count参数少于sizeOfBuffer - 1时, 相当于有些可用的buffer也故意不让使用,这种用法有些奇怪。应该没人会这么用吧。

template <size_t size>

int _snprintf_s( char (&buffer)[size], size_t count, const char *format [, argument] ... ); // C++ only

这是_snprintf_s在c++的另外一个重载版本, char数组,通过模板获取到缓冲区的大小,可以少填长度参数。 跟原版功能是一样的。


6.

int _sprintf_l( char *buffer, const char *format, locale_t locale [, argument] ... );

locale:The locale to use.

由于国家/语言的差异, 有些用于格式化的函数的行为在不同语言里并不相同。

例如我们浮点数表示"3.5", 但是在德国,浮点数不是用'.'分隔,而是用','。因此在德语中,应该表示为“3,5”。

这样atof函数在两个国家的格式化结果应该是不一样的。因此微软准备了一系列的_XXX_l函数,比正常的函数多一个locale_t参数。例如_sprintf_l对应sprint, _atof_l对应atof, _atoi_l对应atoi。而默认的XXX函数实际上底层调用的是_XXX_l(locale=NULL)。

因此sprintf函数底层应该是调用了_sprintf_l(buffer, format, NULL, ...)。

atof(str)实际上调用了_atof_l(str, NULL),  atoi(str)实际上调用了_atoi_l(str, NULL)。

更多与地区相关的函数参考https://docs.microsoft.com/en-us/cpp/c-runtime-library/locale?view=msvc-150

同理

int _snprintf_s_l( char *buffer, size_t sizeOfBuffer, size_t count, const char *format, locale_t locale [, argument] ... );

相当于_snprintf_s + _l。

另外由于宽字符wchar_t的存在,每一个对char的字符串操作都有一个宽字符的对应版本的函数

宽字符以及locale两两组合,每个函数都会出现4个版本。 如果是_snprintf等有sizeOfBuffer参数的, 通过模板重载自动识别sizeOfBuffer,函数形式再多一倍。

sprintf:

int sprintf( char *buffer, const char *format [, argument] ... );
int _sprintf_l( char *buffer, const char *format, locale_t locale [, argument] ... );
int swprintf( wchar_t *buffer, size_t count, const wchar_t *format [, argument]... );
int _swprintf_l( wchar_t *buffer, size_t count, const wchar_t *format, locale_t locale [, argument] ... );
template <size_t size>
int sprintf( char (&buffer)[size], const char *format [, argument] ... );
template <size_t size>
int _sprintf_l( char (&buffer)[size], const char *format, locale_t locale [, argument] ... ); 

sprintf_s:

int sprintf_s( char *buffer, size_t sizeOfBuffer, const char *format [, argument] ... );
int _sprintf_s_l( char *buffer, size_t sizeOfBuffer, const char *format, locale_t locale [, argument] ... );
int swprintf_s( wchar_t *buffer, size_t sizeOfBuffer, const wchar_t *format [, argument] ... );
int _swprintf_s_l( wchar_t *buffer, size_t sizeOfBuffer, const wchar_t *format, locale_t locale [, argument] ... );
template <size_t size>
int sprintf_s( char (&buffer)[size], const char *format [, argument] ... );
template <size_t size>
int swprintf_s( wchar_t (&buffer)[size], const wchar_t *format [, argument] ... );

snprintf:

int snprintf( char *buffer, size_t count, const char *format [, argument] ... );
int _snprintf( char *buffer, size_t count, const char *format [, argument] ... );
int _snprintf_l( char *buffer, size_t count, const char *format, locale_t locale [, argument] ... );
int _snwprintf( wchar_t *buffer, size_t count, const wchar_t *format [, argument] ... );
int _snwprintf_l( wchar_t *buffer, size_t count, const wchar_t *format, locale_t locale [, argument] ... );
template <size_t size>
int _snprintf( char (&buffer)[size], size_t count, const char *format [, argument] ... );
template <size_t size>
int _snprintf_l( char (&buffer)[size], size_t count, const char *format, locale_t locale [, argument] ... );
template <size_t size>
int _snwprintf( wchar_t (&buffer)[size], size_t count, const wchar_t *format [, argument] ... );
template <size_t size>
int _snwprintf_l( wchar_t (&buffer)[size], size_t count, const wchar_t *format, locale_t locale [, argument] ... );

snprintf_s:

int _snprintf_s( char *buffer, size_t sizeOfBuffer, size_t count, const char *format [, argument] ... );
int _snprintf_s_l( char *buffer, size_t sizeOfBuffer, size_t count, const char *format, locale_t locale [, argument] ... );
int _snwprintf_s( wchar_t *buffer, size_t sizeOfBuffer, size_t count, const wchar_t *format [, argument] ... );
int _snwprintf_s_l( wchar_t *buffer, size_t sizeOfBuffer, size_t count, const wchar_t *format, locale_t locale [, argument] ... );
template <size_t size>
int _snprintf_s( char (&buffer)[size], size_t count, const char *format [, argument] ... );
template <size_t size>
int _snwprintf_s( wchar_t (&buffer)[size], size_t count, const wchar_t *format [, argument] ... );

sprintf, snprintf, _snprintf, sprintf_s 等的区别相关推荐

  1. 各种输出函数的比较(printf/fprintf/sprintf/snprintf/vprintf/vfprintf/vsprintf/vsnprintf)

    对于程序猿来说,printf函数可以说是最熟悉的一个工具了.利用它可以将各类调试信息输出到指定的设备(比如串口)中,实现对程序运行状态的掌控和分析.不过,在实际的应用中,相信大家除了printf函数之 ...

  2. 三个打印函数printf()/sprintf()/snprintf()区别

    先贴上其函数原型 printf( const char *format, ...)    格式化输出字符串,默认输出到终端-----stdout sprintf(char *dest, const c ...

  3. linux下snprintf用法,关于snprintf,_snprintf,_snprintf_s操作

    一.snprintf与snprintf_s的区别 众所周知,sprintf不能检查目标字符串的长度,可能造成众多安全问题,所以都会推荐使用snprintf. snprintf函数在C++11之前 并不 ...

  4. printf,fprintf(stdout,stderr),sprintf等的使用方法及区别

    名称 描 述 例 子 stdin 标准输入 键盘 stdout 标准输出 屏幕 stderr 标准错误 屏幕 stdprn 标准打印机 LPT1端口 stdaux 标准串行设备 COM1端口 1,pr ...

  5. sprintf 和 snprintf区别

    sprintf 和snprintf 函数区别 sprintf 函数定义见: https://cplusplus.com/reference/cstdio/sprintf/ snprintf 函数定义见 ...

  6. snprintf与_snprintf区别

    某天在处理公司某个项目时,偶然发现snprintf为啥是红色的 正常来讲source insight中默认配置中系统函数的颜色是黑色的,红色为宏定义 这就激起了我的好奇(摸鱼)心态 然后我follow ...

  7. 关于sprintf和snprintf的比较

    #include <stdio.h> #include <string.h>typedef unsigned char uchar;#define BUF_SIZE 10 // ...

  8. snprintf()函数探讨

    printf()/sprintf()/snprintf()区别  先贴上其函数原型 printf( const char *format, ...)    格式化输出字符串,默认输出到终端-----s ...

  9. Qt sprintf_s函数格式化字符串出错

    Qt sprintf_s函数格式化字符串出错 问题的出现: 我在VS上用c C++写的跨平台的函数 移植到Qt 上面 出现sprintf_s 函数格式化出错. 开始以为是编码问题  反复查找Qt乱码问 ...

最新文章

  1. Ajax检测注册用户是否存在
  2. Java中String、StringBuffer和StringBuilder的区别
  3. python 之路200行Python代码写了个打飞机游戏
  4. Java 一组温度从摄氏到华氏的转换
  5. 牛客网(剑指offer) 第十三题 调整数组顺序使奇数位于偶数前面
  6. 44. Wildcard Matching 通配符匹配
  7. 使用ABAP Push Channel(APC)开发的乒乓球游戏,可双打
  8. 食物链 POJ - 1182(带权并查集模板)
  9. uva 242——Stamps and Envelope Size
  10. php中二进制函数,PHP-----函数和二进制
  11. synchronized底层是如何实现的?
  12. 专业英语笔记:Spring框架
  13. 破解电信光猫RG2010-CA超级管理员账号
  14. 服务器如果选用CPU
  15. 苹果id退出后数据还在吗_【保留数据修复】升级iOS 14后,出现白苹果的修复方法!...
  16. oracle18c默认sid,Oracle 18c-可伸缩序列(Scalable Sequence)
  17. pythonif多个条件同时满足_Python基础:条件控制if
  18. SpringBoot+Maven 多模块项目的构建、运行、打包实战
  19. 从马尔科夫吸收链看美剧季数
  20. Javaweb-超市订单管理系统SMBMS

热门文章

  1. GitHub 上超屌的 9 个 Vue 开源项目
  2. CrowdHuman数据集转成VOC格式并训练模型
  3. 机器人竟会写诗,诗人们大呼不敢相信!
  4. Windows10计算机无法启动,Win10电脑无法开机提示Windows boot manager
  5. 用 CaptureScreenshot捕捉游戏画面(截图,截屏)
  6. Spring Security登录用户数据获取(4)
  7. linux版本kettle 中文乱码,kettle工具同步数据乱码-Linux下乱码问题二
  8. Codeforces Round #717 Div.2
  9. Android 11.0 当安装多个播放器时,设置默认播放器播放歌曲
  10. 源站IP暴露了怎么自查?