C++中printf包装器如何传递参数包至make_buffer_fmt?
解决printf包装函数的可变参数传递问题
要解决你遇到的参数包传递问题,核心是为每个可变参数函数提供对应的va_list重载版本,这样既能保留直接传可变参数的便捷性,又能避免重复写va_start/va_end的样板代码,同时解决非法传递va_list导致的内存读取错误。
步骤1:为make_buffer_fmt添加va_list版本
先实现接收va_list的底层函数,原可变参数版本调用这个函数,复用核心逻辑:
#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> // 底层va_list版本,负责实际的缓冲区构建 char* make_buffer_fmt_va(char foo, const char* fmt, va_list va) { // 先计算所需缓冲区大小 va_list va_copy; va_copy(va_copy, va); int size = vsnprintf(NULL, 0, fmt, va_copy); va_end(va_copy); if (size < 0) return NULL; char* buf = malloc(size + 1); if (!buf) return NULL; vsnprintf(buf, size + 1, fmt, va); return buf; } // 原可变参数版本,保留便捷调用方式 char* make_buffer_fmt(char foo, const char* fmt, ...) { va_list va; va_start(va, fmt); char* buf = make_buffer_fmt_va(foo, fmt, va); va_end(va); return buf; }
步骤2:为write_fmt添加va_list版本
同样拆分出va_list版本,原可变参数版本调用它,同时让va_list版本复用make_buffer_fmt_va:
#include <unistd.h> // va_list版本的write_fmt,供上层函数传递参数包 int write_fmt_va(int fd, char foo, const char* fmt, va_list va) { char* buf = make_buffer_fmt_va(foo, fmt, va); if (!buf) return -1; int ret = write(fd, buf, strlen(buf)); free(buf); return ret; } // 原可变参数版本,支持直接调用如write_fmt(fd, foo, "Hello %s", "World") int write_fmt(int fd, char foo, const char* fmt, ...) { va_list va; va_start(va, fmt); int ret = write_fmt_va(fd, foo, fmt, va); va_end(va); return ret; }
步骤3:实现简洁的log函数
现在log函数只需处理自身的va_list,调用write_fmt_va即可,无需重复冗余代码:
#define LOG_FILENO STDOUT_FILENO #define LOG_INFO 'I' int log_info(const char* fmt, ...) { va_list va; va_start(va, fmt); int ret = write_fmt_va(LOG_FILENO, LOG_INFO, fmt, va); va_end(va); return ret; }
关键说明
- 你之前的错误在于直接将va_list传给可变参数
...:write_fmt的...期望的是可变参数列表,而非va_list对象,这样传递会导致函数错误地把va_list当作普通参数解析,进而读取随机内存。 - 拆分出va_list版本后,所有上层函数只需做一次va_start/va_end,底层逻辑复用va版本实现,彻底消除重复样板。
- 保留原可变参数版本,依然支持直接调用
write_fmt(LOG_FILENO, LOG_INFO, "Hello %s", "World")的便捷写法。
内容的提问来源于stack exchange,提问作者mj2
相关产品推荐
相关产品推荐

