空了一段时间…不过还是得完成记录
第二十四个。(就快写完注释了,结果一点保存全没了…什么奇葩。重写一遍吧:)该函数的作用和第二十五个函数一样,就是将fmt
这串字符格式化后保存到s
中并返回它的地址。我们一般直接使用第二十五个函数,这个函数是给第二十五个函数调用的。主要涉及到两个知识点-变参函数和va_list
这个参数(来自<stdarg.h>
),网上搜索相关内容看下,再用下sprintf()
这个函数,会比较好理解下面的代码。
// 作者注释
/* Like sdscatprintf() but gets va_list instead of being variadic. */
// 返回的地址和s变量的地址一样的,这样做是为了实现链式操作
// 比如我们可以将这个函数作为参数传入另一个函数中
sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
// va_list这个参数具体原理我..也不是很清楚
// 不过用法都差不多,如果我们自己写变参函数,先照着这样写
va_list cpy;
char staticbuf[1024], *buf = staticbuf, *t;
// 计算需要格式化的字符串的个数
// 注意fmt里头可能含有'%d'这样的格式,它占两个字符
// *2是为了一次申请更多的空间,避免以后空间不够又重新申请,提高效率
size_t buflen = strlen(fmt)*2;
/* We try to start using a static buffer for speed.
* If not possible we revert to heap allocation. */
// 就像作者上面说的,为了提高效率,先使用栈上的空间,如果不够
// 就重新在堆中申请空间
if (buflen > sizeof(staticbuf)) {
// 需要格式化的字符串的空间大于staticbuf的空间
// buf本来指向staticbuf,现在指向新申请的空间
buf = s_malloc(buflen);
if (buf == NULL) return NULL;
} else {
// staticbuf的空间足够大
// buflen赋值为staticbuf的大小,也就是1024
buflen = sizeof(staticbuf);
}
/* Try with buffers two times bigger every time we fail to
* fit the string in the current buffer size. */
// 下面的循环做了两件事-格式化fmt并保存到buf中
// 如果buf的空间不够大,重新为buf申请空间
// 这不是死循环,因为后面有个break
while(1) {
// 将buf指向的地址的倒数第3个字符赋值为空
// 这样做是为了判断buf的空间够不够大
buf[buflen-2] = '\0';
// 下面的函数将ap参数的内容拷贝到cpy中,先不用纠结为啥样这样做
// 假装这里就是得这样做:)
va_copy(cpy,ap);
// 下面这个函数是标准库中的函数
// 它的作用是将buflen个格式化后的fmt字符保存到buf中
// 作者的函数作用和这个差不多,为什么还要重写一个
// 因为作者的函数还做了其它事
vsnprintf(buf, buflen, fmt, cpy);
// 格式化完成后记得调用下面这个函数
va_end(cpy);
// 下面的代码块就是做的其它事了
// 前面buf[buflen-2]赋值为空了,经过上面的格式化保存后
// 如果buf的空间比需要保存的字符串小,那它就不是空了
if (buf[buflen-2] != '\0') {
// 如果不为空,说明buf空间不够
// 如果buf不是指向staticbuf的地址,那就是指向了堆中申请的空间
// 但是这里空间又不够,就先释放掉这段空间
if (buf != staticbuf) s_free(buf);
// 需要重新申请的空间扩大为之前的两倍
buflen *= 2;
// 在堆中申请空间
buf = s_malloc(buflen);
// 申请失败,返回空
if (buf == NULL) return NULL;
// 申请成功,重新执行整个循环
// 为什么要重新执行一遍?当然是为了将格式化后的字符串保存到buf中
// 如果buf[buflen-2]为空,说明buf的空间够了
// 就不会执行整个if语句块了,并跳出while循环
continue;
}
break;
}
/* Finally concat the obtained string to the SDS string and return it. */
// 经过上面的代码,已经成功将字符串格式化并保存到buf中了
// t是一个空指针,会指向下面这个函数申请的地址
// 函数原理前面章节有讲,作用是将buf这串字符合并到s后面
// t指向的地址就是s指向的地址
t = sdscat(s, buf);
// 已经将buf的内容保存到s中了
// 如果buf指向的是堆中空间,释放它
if (buf != staticbuf) s_free(buf);
return t;
}
第二十五个。(终于注释完了第二十四个:)其实之前是接着记录的,到第二十四个的时候,看了下不太懂,就空了一段时间了。
// 作者注释
/* Append to the sds string 's' a string obtained using printf-alike format
* specifier.
*
* After the call, the modified sds string is no longer valid and all the
* references must be substituted with the new pointer returned by the call.
*
* Example:
*
* s = sdsnew("Sum is: ");
* s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
*
* Often you need to create a string from scratch with the printf-alike
* format. When this is the need, just use sdsempty() as the target string:
*
* s = sdscatprintf(sdsempty(), "... your format ...", args);
*/
// 该函数作用和第二十四个一样
// 用法'sds s = sdscatprintf(sdsempty(), "abc%d", 1);'
// 那么'(s == "abc1")',注意第一个参数不是传入s,而是sdsempty()
// 因为第一个参数的空间地址必须合法才能格式化成功
sds sdscatprintf(sds s, const char *fmt, ...) {
// 假装这里必须声明一个这样的变量:)
va_list ap;
// 作者这里为啥不直接这样声明sds t;
char *t;
// va_start()和va_end()是一对
// 用来处理变参
va_start(ap, fmt);
// 调用第二十四个函数
t = sdscatvprintf(s,fmt,ap);
va_end(ap);
// 格式化成功
return t;
}
先记录到这。第二十六个函数内容有点多,放下次。:)