str: 目的内存空间地址;
size: 最大可用的目的内存空间大小;
format:格式化字符串(类似printf);
... : 可选参数,与format中的占位符相关(类似printf).
函数功能该函数可以将不同的内容(ex. int)按照期望的既定格式打印成字符串,并保存到buffer中。
比如需要将十进制数 20 转换为两bytes十六进制字符串的时候,可以使用该函数实现。
int ret;char hex[10];ret = snprintf(hex, sizeof(hex), "0X%04X", 20);"0X%04X" 表示会先加上0X前缀,通常用来表示是十六进制;后面%04X是指按大写十六进制打印,且需要占据4位字符的宽度,不够4位时补0。所以最终的字符串格式会是“0X0014”。
特别注意1. 无论何时一定会在字符串末尾添加' ' 字符串结束符。
当字符串长度小于size时,会直接在字符串的末尾添加' ';当字符串长度等于size时,会取前size-1个字符,并在末尾添加‘ ’(即:加上' '后,一共size个字符);当字符串长度大于size时,只取前size-1个字符,并在末尾添加' '(即:加上' '一共size个字符);综上:snprintf不仅一定会自动在字符串末尾添加' ',而且一定会尊重指定长度size的大小,不会超过该长度(算上自动在末尾增加的' ')。所以从这一点来讲,使用snprintf是相对比较安全的,不用担心会出现overflow的情况。
NOTE:这一点上,snprintf与strncpy是有区别的。strncpy虽然也会指定目的内存的大小,但是当源字符串长度大于指定的内存大小时,strncpy虽然在达到指定大小后会停止copy,但是并不会在末尾自动添加' '。这样,在访问字符串时,有可能出现overflow的情况,因为末尾没有字符串结束符' '。
思考:
(1)当目的内存实际长度为N1,但是在使用snprintf时指定的实际size(第二个参数)为N2, 且N2<N1,可能会出现什么情况?
(2)如果N2>N1,又可能出现什么情况?
2. 特别小心返回值。
当出现错误时,会返回负值;当字符串长度小于size时,返回打印到目的内存的实际字符串长度(不包括' ');当字符串长度大于等于size时,尽管字符串会被截断(只有size-1个字符串被打印到目的内存),但是返回值却会返回源字符串的实际长度(即假设目的内存无限大,总是能写下源字符串)。综上:snprintf的返回值可能大于或等于指定的size,这时候说明目的内存不够大,源字符串被截断,需要小心处理,这是否是期望发生的情形。
NOTE:有一些写法会利用snprintf的这个特点来确定合适的目的内存大小,例如:
const char *fmt = "sqrt(2) = %f";char * buf;int sz = snprintf(NULL, 0, fmt, sqrt(2));buf = (char *)malloc(sz+1); // +1 for append ' 'snprintf(buf, sz+1, fmt, sqrt(2)); 实践在实际使用snprintf函数时,应该根据自己的实际需求,来判断如何合理的使用。
通常的用法会类似如下。
include <stdio.h>include <stdlib.h>#define BUF_LEN 20char buf[BUF_LEN];int year = 2019;int month = 12;int day = 13;int ret;ret = snprintf(buf, sizeof(buf), "%d-%02d-%02d", year, month, day);if(ret <0) { // error handling} else if(ret >= sizeof(buf)) { // buffer isn't enough, error handling} else { // expected case} 更进一步snprintf是一个在C99才被加入如标准的函数,原来的各个编译器都有自己的实现,至少.NET2003编译器还要是使用_snprintf这样的函数名称。而这些编译器间都有差异,而且Glibc库又有自己的不同的实现。如果使用一些比较旧的编译器或者不同平台的编译器,你会发现,如果传递的buf的长度不够的情况下,有可能null-terminator都没有加入,那么你使用的时候还是可能溢出,而且返回值的判断在不同的平台也可能不一样。
所以如果你想编一个可以跨平台的安全且正确的snprintf,应该怎么做呢?
以下只是一个可能的参考。
#define MAX_LEN 32char buffer[MAX_LEN];char* source = "xxxxxxx";size_t buf_len = sizeof(buffer) - 1;int len = snprintf(buffer, buf_len, "%s", source); if ((len < 0) || (len > max_len)) { // error handling .... printf("error or overflow!n"); } else { // expected case buffer[max_len] = 0; }
参考文章:
[1] https://linux.die.net/man/3/snprintf
[2] http://joequery.me/code/snprintf-c/
[3] https://cloud.tencent.com/developer/article/1546579
[4] https://cloud.tencent.com/developer/article/1021143