写出完美的snprintf

平时公司的代码安全扫描会给出不安全代码的告警,其中会检查代码中间的strcpy和sprintf函数,而要求使用strncpy和snprintf。今天我们讨论一下怎样写出完美的snprintf。

snprintf是一个在C99才被加入如标准的函数,原来的各个编译器都有自己的实现,至少.NET2003编译器还要是使用_snprintf这样的函数名称。

而这些编译器间都有差异,而且Glibc库又有自己的不同的实现。

查询一下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, then len characters are stored in buffer, a null-terminator is appended, and len is returned.

If len = count, then len characters are stored in buffer, no null-terminator is appended, and len is returned.

If len > count, then count characters are stored in buffer, no null-terminator is appended, and a negative value is returned.

If buffer is a null pointer and count is nonzero, or format is a null pointer, the invalid parameter handler is invoked, as described in Parameter Validation. If execution is allowed to continue, these functions return -1 and set errno to EINVAL.

For information about these and other error codes, see _doserrno, errno, _sys_errlist, and _sys_nerr.

当buffer长度不够时,返回的是负数。

而LINUX的说明如下:

Return value

Upon  successful  return,  these  functions  return  the number of characters printed (not

including the trailing ‘\0‘ used to end output to strings).  The functions snprintf()  and

vsnprintf()  do not write more than size bytes (including the trailing ‘\0‘).  If the out-

put was truncated due to this limit then the return value is the number of characters (not

including  the  trailing ‘\0‘) 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.)  If an output error is encountered, a negative

value is returned.

NOTES

The glibc implementation of the functions snprintf() and vsnprintf() conforms to  the  C99

standard,  i.e.,  behaves  as  described above, since glibc version 2.1. Until glibc 2.0.6

they would return -1 when the output was truncated.

在比较新的版本中,其遵守C99的规范,当buffer长度不够时,返回的是超过Buffer长度的正数。

你会发现,如果传递的buf的长度不够的情况下,null-terminator都没有加入。。。。。那么你使用的时候还是可能溢出。而且返回值的判断在不同的平台还可能不一样。

当然我理解使用snprintf的主要好处在于安全性,但是如果使用不对仍然可能有悲剧发生,比如你的更新SQL语句被截断了WHERE条件。所以返回值还是要判断。

那么最简单的方法还是传递的给snprintf的长度参数count应该buf长度-1,然后还要将最后一个字符改为null-terminator。然后再加入相应的判断。

发现返回值小于0或者大于(可能有等于,看你传递的长度参数和Buffer的关系)实际长度时认为出现问题。

经过测试的正确写法:

max_len = sizeof(buf)-1;

len = snprintf(buf, max_len, ...);

if ((len < 0) || (len > max_len))

{

//错误处理

}

else

{

buf[max_len]=0;

do job...

}

代码说明:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>  

int main(int argc, char** argv)
{
    if (argc < 2)
    {
        printf("usage:./snprintf_perfect xxxx\n");
        return -1;
    }
    char buffer[16];
    size_t max_len = sizeof(buffer) - 1;
    int len = snprintf(buffer, max_len, "%s", argv[1]);
    if ((len < 0) || (len > max_len))
    {
        printf("overflow!!\n");
    }
    else
    {
        buffer[max_len] = 0;
        printf("%s\n", buffer);
    }

    return 0;
}

结果说明:

# 长度为buffer长度-1

[[email protected] linux_programming]# ./snprintf_perfect 012345678901234

01234567890123

# 长度为buffer长度,溢出!

[[email protected] linux_programming]# ./snprintf_perfect 0123456789012345

overflow!!

时间: 2024-11-08 06:10:37

写出完美的snprintf的相关文章

不要把时间浪费在写出完美的代码

一个系统可能会持续工作5年,10年,20年甚至更长的时间.但是具体到这个系统中的某一行代码,即使是关于设计的部分,这一行代码存在的时间却会很短:几个月或者几天,甚至是几分钟. 一些代码比其他代码更重要 通过研究代码是怎么随时间改变的,Michael Feathers定义了一条代码变动曲线.每个系统都有很多写完之后就不再改变的代码.与此同时,也存在少量这样的代码,这些代码是整个系统最重要也是最有用的代码,它们会随时间一次又一次地改变.重构,或者被删除,重新来过,如是反复几次. 随着你对一个系统越来

在java中写出完美的单例模式

1. 前言 单例(Singleton)应该是开发者们最熟悉的设计模式了,并且好像也是最容易实现的--基本上每个开发者都能够随手写出--但是,真的是这样吗? 作为一个Java开发者,也许你觉得自己对单例模式的了解已经足够多了.我并不想危言耸听说一定还有你不知道的--毕竟我自己的了解也的确有限,但究竟你自己了解的程度到底怎样呢?往下看,我们一起来聊聊看~ 2. 什么是单例? 单例对象的类必须保证只有一个实例存在--这是维基百科上对单例的定义,这也可以作为对意图实现单例模式的代码进行检验的标准. 对单

写出完美论文的十个技巧10 Tips for Writing the Perfect Paper

10 Tips for Writing the Perfect Paper Like a gourmet meal or an old master painting, the perfect college paper is carefully constructed – not thrown together the night before it’s due.   Each part is just right, and the pieces are assembled to form t

写出优美代码的方式,两个习惯:一步到位VS迭代优化

最近把手头这个安卓APP的所有事务性方法都写完了,有了以下体会,新手体会,老鸟轻拍 想写成优美代码的人一般都会有这样的想法: 一定要在写每一句代码,写每一个方法,构造每一个类的时候,都要记得优化:解耦以复用,拆分方法以复用,使用循环减少冗余,限制循环次数减少无效操作,等等.. 这个想法一定没有错,但很多时候往往会是这样的情况: 当功能一复杂,比如你已经分解了几个方法,比如你已经使用了几层循环(有点过分...),比如在多线程中 你经常无法一步到位地完成那么多优化 这往往造成你写一句代码会思考很久

我的Java历程_写出这个数

lzJava基础进行中,今天偶然间看到的一个题目: 读入一个自然数n,计算其各位数字之和,用汉语拼音写出和的每一位数字.如下代码: import java.util.*;public class Main2{ public static void main(String[] args){ String[] date = {"Ling", "Yi", "Er", "San", "Si", "Wu&q

代码示例:一些简单技巧优化JavaScript编译器工作详解,让你写出高性能运行的更快JavaScript代码

告诉你一些简单的技巧来优化JavaScript编译器工作,从而让你的JavaScript代码运行的更快.尤其是在你游戏中发现帧率下降或是当垃圾回收器有大量的工作要完成的时候. 单一同态: 当你定义了一个两个参数的函数,编译器会接受你的定义,如果函数参数的类型.个数或者返回值的类型改变编译器的工作会变得艰难.通常情况下,单一同态的数据结构和个数相同的参数会让你的程序会更好的工作. function example(a, b) { // 期望a,b都为数值类型 console.log(++a * +

掌握解决问题的艺术,学会迭代开发,成为协作开发的专家,然后为写出更好的代码而担忧(转)

很多开发人员普遍犯有一个错误,认为他们的工作就是写代码.这不是一个开发人员要做的事情. 一个开发人员的工作是解决问题. 解决问题的一部分过程通常涉及到写代码,但是这部分工作是非常非常小的.开发有用的东西才花更多时间. 明白如何迭代开发,随着对问题有更好的理解,你才能给难题增加一些小功能,因为从头开发完美的产品是不可能的.不用写代码就能验证功能,因为很明显,写代码是相当昂贵的. 用于测试.评测和抛弃想法的系统也是极其重要的,因为要是没有它,整个开发组将耗费越来越多的精力,还有用来帮助他们执行得更有

【技能】使用纯CSS+html写出方向箭头,简单大方,好看

使用纯CSS+html写出方向箭头,贴出来就可以用,100%原创 <html> <head> <title></title> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <style type="text/css"> .pointsRule{ display: inline-blo

写出一个缓存系统的伪代码001

/** * 写出一个缓存系统的伪代码 * @author ysloong * */ public class CacheDemo { private Map<String, Object> map = new HashMap<String, Object>(); public static void main(String[] args) { // TODO Auto-generated method stub } public synchronized Object getDat