snprintf笔记
在weibo上看到Laruence大神修复了一个使用snprintf的bug (http://t.cn/Rm6AuFh) 引起了TK教主的关注。TK教主着重提到了在windows下snprintf与_snprintf的行为有差别。
想想自己之前也在windows下写过代码,因具体的使用场景没有触发这种差异,因而对此也没有特别留意。下面对此写代码和查MSDN了具体验证了差别,结果记录如下。
先说snprintf,相信只要写过C代码的程序员,肯定用过这个C库函数,其声明如下
int snprintf(char *str, size_t size, const char *format, ...);
其向 str 为起始地址,长度为 size 的buffer中,按 format 指定的格式进行格式化写入串。size 限制了最大向 str 写入的字节数,但具体根据 format 格式和给定的额外参数,实际拼接出的串长度会超过 size。此时的结果就需要特别注意。man中对snprintf的说明如下
Upon successful return, these functions return the number of characters printed (excluding the null byte used to end output to strings).
The functions snprintf() and vsnprintf() do not write more than size bytes (including the terminating null byte ('\0')). If the output was truncated due to
this limit, then the return value is the number of characters (excluding the terminating null byte) which would have been written to the final string if
enough space had been available. Thus, a return value of size or more means that the output was truncated. (See also below under NOTES.)
返回值是要特别注意的,并不是是向 str 写入的字节数,而是 format 和对应实参拼接出的串长度(称为len, 不包含尾部0字节)。
当 len 小于 size 时,除了将该串写入buffer之外,还额外追加一个0字节。
当 len 等于 size 时,将该串写入buffer之后,会将最后字节清零,该串位内容未字节也就被截断了。
当 len 大于 size 时,最多写入该串前(size-1)个字节,并将第 size 个字节清零。
snprintf 在 linux 下(libc-2.23.so) 和 windows 下(VS2015 VCRUNTIME140)行为一致,都是如上所述。
但在windows下 _snprintf 的行为和snprintf不完全一致。
当 len 小于 size 时,除了将该串写入buffer之外,还额外追加一个0字节,和 snprintf 相同
当 len 等于 size 时,直接将串写入buffer,并不会对尾字节清零,返回值为 len,和 snprintf 不同
当 len 大于 size 时,直接将串写入buffer,并不将第 size 个字节清零,返回值为 -1,和 snprintf 不同
MSDN对应的说明如下
Let len be the length of the formatted data string, not including the terminating null. len and count are in bytes for _snprintf, wide characters for _snwprintf.
If len < count, len characters are stored in buffer, a null-terminator is appended, and len is returned.
If len = count, len characters are stored in buffer, no null-terminator is appended, and len is returned.
If len > count, count characters are stored in buffer, no null-terminator is appended, and a negative value is returned.
实际使用如下代码进行测试,结果如下,注释中是 GDB 查看的中间结果。
#include <stdio.h>
#include <string.h>
#ifdef WIN32
#define snprintf _snprintf
#endif
int main(int argc, char *argv[])
{
char buffer[16];
int ret;
/*
(gdb) p ret
$1 = 4
(gdb) x /16bx buffer
0x7ffffffde250: 0x31 0x32 0x33 0x34 0x00 0x01 0x01 0x01
0x7ffffffde258: 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01
*/
memset(buffer, 1, sizeof(buffer));
ret = snprintf(buffer, 8, "%s", "1234");
/*
(gdb) p ret
$2 = 8
(gdb) x /16bx buffer
0x7ffffffde250: 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x00
0x7ffffffde258: 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01
*/
memset(buffer, 1, sizeof(buffer));
ret = snprintf(buffer, 8, "%s", "12345678");
/*
(gdb) p ret
$3 = 9
(gdb) x /16bx buffer
0x7ffffffde250: 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x00
0x7ffffffde258: 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01
*/
memset(buffer, 1, sizeof(buffer));
ret = snprintf(buffer, 8, "%s", "123456789");
return 0;
}
而在VS下,查看的中间结果, snprintf 和 _snprintf 分别如下
snprintf
ret = 4
buffer: 31 32 33 34 00, 01 01 01 01 01 01 01 01 01 01 01
ret = 8
buffer: 31 32 33 34 35 36 37 00, 01 01 01 01 01 01 01 01
ret = 9
buffer: 31 32 33 34 35 36 37 00, 01 01 01 01 01 01 01 01
_snprintf
ret = 4
buffer: 31 32 33 34 00, 01 01 01 01 01 01 01 01 01 01 01
ret = 8
buffer: 31 32 33 34 35 36 37 38, 01 01 01 01 01 01 01 01
ret = -1
buffer: 31 32 33 34 35 36 37 38, 01 01 01 01 01 01 01 01
snprintf笔记的更多相关文章
- 《Linux/Unix系统编程手册》读书笔记7 (/proc文件的简介和运用)
<Linux/Unix系统编程手册>读书笔记 目录 第11章 这章主要讲了关于Linux和UNIX的系统资源的限制. 关于限制都存在一个最小值,这些最小值为<limits.h> ...
- sc7731 Android 5.1 LCD驱动简明笔记之三
此篇笔记基于sc7731 - android 5.1,对lcd的gralloc库做一个简明笔记. 第一部分 调用gralloc.sc8830.so所谓的Gralloc模块,它就是一个模块,一个操作ke ...
- 错误内存【读书笔记】C程序中常见的内存操作有关的典型编程错误
题记:写这篇博客要主是加深自己对错误内存的认识和总结实现算法时的一些验经和训教,如果有错误请指出,万分感谢. 对C/C++程序员来讲,内存管理是个不小的挑战,绝对值得慎之又慎,否则让由上万行代码构成的 ...
- Linux进程间通信IPC学习笔记之管道
基础知识: 管道是最初的Unix IPC形式,可追溯到1973年的Unix第3版.使用其应注意两点: 1)没有名字: 2)用于共同祖先间的进程通信: 3)读写操作用read和write函数 #incl ...
- redis 学习笔记一
找了半天,发觉还是redis的源码看起来比较舒服.所以决定今年把redis的源码读一遍顺便做个读书笔记.好好记录下.话说现在越来不越不愿意用脑袋来记录东西,喜欢靠note来记.话说这样不爱用脑会不会过 ...
- 用gdb调试程序笔记: 以段错误(Segmental fault)为例
用gdb调试程序笔记: 以段错误(Segmental fault)为例[转] 1.背景介绍2.程序中常见的bug分类3.程序调试器(如gdb)有什么用4.段错误(Segmental fault)介绍5 ...
- 树莓派学习笔记——使用文件IO操作GPIO SysFs方式
0 前言 本文描写叙述假设通过文件IO sysfs方式控制树莓派 GPIO端口.通过sysfs方式控制GPIO,先訪问/sys/class/gpio文件夹,向export文件写入GPIO编号, ...
- 【unix网络编程第三版】阅读笔记(二):套接字编程简介
unp第二章主要将了TCP和UDP的简介,这些在<TCP/IP详解>和<计算机网络>等书中有很多细致的讲解,可以参考本人的这篇博客[计算机网络 第五版]阅读笔记之五:运输层,这 ...
- Muduo学习笔记(一) 什么都不做的EventLoop
Muduo学习笔记(一) 什么都不做的EventLoop EventLoop EventLoop的基本接口包括构造.析构.loop(). One Loop Per Thread 一个线程只有一个Eve ...
随机推荐
- A、B两个线程交替打印1 -- 100
方案一:import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.u ...
- GDAL——命令使用专题——gdallocationinfo命令
GDAL——命令使用专题——gdallocationinfo命令 前言 GDAL(Geospatial Data Abstraction Library)是一个在X/MIT许可协议下的开源栅格空间数 ...
- CSS3绘制特殊图形
- APP压力稳定性测试之monkey环境搭建
一.搭建adb环境: 需要的安装软件包可以使用我分享的,链接:https://pan.baidu.com/s/13DThDtc0GALabTakshcLfg 密码:0kuo:也可以自己百度下载 1)下 ...
- Angular No name was provided for external module 'XXX' in output.globals 错误
Angular 7 开发自定义库时,引用ngZorroAntd,build过程中出现 No name was provided for external module 'ng-zorro-antd' ...
- input 特殊字符限制
ng-pattern="/^[A-Za-z0-9_,\.\u4e00-\u9fa5\s]+$/"
- Excel文件上传功能实现
$(function(){ let file; //上传点击事件 $('#btn').bind('click', function(){ let val = $('#fb').filebox('get ...
- Intellij idea配置及安装插件小记一二
1.项目创建慢及控制台乱码解决. -DarchetypeCatalog=internal:项目骨架采用内部,解决Maven项目创建生成慢: -Dfile.encoding=GB2312:控制台用Sys ...
- docker学习---第一章节
一.docker的应用场景有哪些 Web 应用的自动化打包和发布 自动化测试和持续集成.发布 在服务型环境中部署和调整数据库或其他的后台应用 从头编译或者扩展现有的OpenShift或Cloud Fo ...
- Alpha阶段敏捷冲刺总结
项目感言 张艺琳 在这次冲刺中,我主要担任着PM的角色.不仅要梳理流程给小组每个成员进行分工,并且还要及时监督他们在要求时间内提交代码.同时我也参与到开发中去,与小组成员一起讨论数据库,最后一起设计出 ...