首页 > 技术文章 > snprintf 返回值陷阱 重新封装

shenlinken 2017-12-16 13:05 原文

snprintf()函数用于将格式化的数据写入字符串,其原型为:

    int snprintf(char *str, int n, char * format [, argument, ...]);
  • str为要写入的目标字符串;
  • n为能写入的字符的最大数目,超过n会被截断,包括'\0'符,所以能最大写入的其实是n-1个字符;
  • format为格式化字符串,使用方式与printf()函数相同;
  • argument为变量,可为多个,取决于format,这里的使用方式与printf相同。
  • 成功则返回参数str 字符串长度(不包括“\0”),失败则返回-1,错误原因存于errno 中。

snprintf()可以认为是sprintf()的升级版,比sprintf()多了一个参数,可以控制要写入的字符串的长度,更加安全,不会造成str的溢出。

不过snprintf也不是尽善尽美的,也有陷阱,当要写入的argument的长度大于str的长度时,返回值不是str的长度,而是argument的长度

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	int len;
	char str[5];
	len = snprintf(str, sizeof(str), "%s", "ABCDEFGH");
	printf("str:%s, len:%d.\n", str, len);

	return 0;
}

输出结果:

str:ABCD, len:8.

返回的长度是8而不是4,不注意的话,下边的使用可能有问题。所以,使用后,下边最好再判断一下len的长度是否超过了size。
其实可以自己封装一个my_snprintf的

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

int my_snprintf(char *dst, int size, char *fmt, ...)
{
	int len;
	va_list argp;
	va_start(argp, fmt);
	len = vsnprintf(dst, size, fmt, argp);
	len = len > size - 1 ? size - 1 : len;
	va_end(argp);
	
	return len;
}

int main(void)
{
	char str[8];
	int len;
	
	len = my_snprintf(str, sizeof(str), "A:%d:%s", 1, "ABCDEFGH");
	printf("str:%s, len:%d\n", str, len);

	return 0;
}

输出:

str:A:1:ABC, len:7

推荐阅读