C++17中如何将vector元素作为可变参数传递给格式化函数?
问题分析与解决方案
核心错误原因
- 可变参数传递完全错误:你的
printVector函数设计为接收可变数量的单个参数(对应格式字符串的占位符),但调用时却传递了vec.data()(uint64_t*指针)和vec.size()(size_t整数)——这两个参数和格式字符串要求的3个占位符完全不匹配,导致vsnprintf错误解析栈上的内存,输出垃圾值。 - 格式占位符与类型不匹配:
%lx对应unsigned long类型,但uint64_t在多数平台上是unsigned long long;%u对应unsigned int,同样和uint64_t类型不兼容,会导致数据截断或解析错误。
解决方案1:C++17+ 类型安全的可变参数展开(通用方案)
利用C++17的std::index_sequence和折叠表达式,将vector的元素逐个展开为可变参数,同时使用标准宏保证格式与类型匹配:
#include <iostream> #include <vector> #include <string> #include <cstdarg> #include <memory> #include <cinttypes> // 提供uint64_t对应的格式宏 #include <utility> // 提供std::index_sequence // 通用可变参数格式化函数 std::string format(const char* fmt, ...) { va_list args; va_start(args, fmt); // 计算所需缓冲区大小 int buf_size = std::vsnprintf(nullptr, 0, fmt, args) + 1; std::unique_ptr<char[]> buf(new char[buf_size]); std::vsnprintf(buf.get(), buf_size, fmt, args); va_end(args); return std::string(buf.get()); } // 模板函数:将vector元素展开为可变参数传递给格式化函数 template<typename T> std::string formatVector(const char* fmt, const std::vector<T>& vec) { // 用index_sequence生成元素索引,逐个展开vector元素 auto invoke_formatter = [&]<std::size_t... I>(std::index_sequence<I...>) { return format(fmt, vec[I]...); }; return invoke_formatter(std::make_index_sequence<vec.size()>()); } int main() { // 使用PRIx64/PRIu64宏匹配uint64_t类型,保证跨平台兼容性 const char* fmt = "SMEM = %" PRIx64 " smId = %" PRIu64 " abId = %" PRIu64; std::vector<uint64_t> vec = {0x1234567890ABCDEF, 2, 4}; std::cout << formatVector(fmt, vec) << '\n'; vec = {0x1234567890ABCD01, 6, 5}; std::cout << formatVector(fmt, vec) << '\n'; return 0; }
解决方案2:固定格式场景下的字符串流方案(更安全)
如果你的输出格式是固定的,直接用std::ostringstream拼接字符串,避免可变参数的不安全问题:
#include <iostream> #include <vector> #include <string> #include <sstream> #include <cinttypes> std::string formatVector(const std::vector<uint64_t>& vec) { if (vec.empty()) return ""; std::ostringstream oss; // 首个元素输出十六进制(带0x前缀) oss << "SMEM = 0x" << std::hex << vec[0]; // 其余元素输出十进制 if (vec.size() >= 2) oss << " smId = " << std::dec << vec[1]; if (vec.size() >= 3) oss << " abId = " << std::dec << vec[2]; return oss.str(); } int main() { std::vector<uint64_t> vec = {0x1234567890ABCDEF, 2, 4}; std::cout << formatVector(vec) << '\n'; vec = {0x1234567890ABCD01, 6, 5}; std::cout << formatVector(vec) << '\n'; return 0; }
运行结果
两种方案都会输出你期望的结果:
SMEM = 1234567890abcdef smId = 2 abId = 4 SMEM = 1234567890abcd01 smId = 6 abId = 5
(注:如果需要大写十六进制,可添加std::uppercase修饰符,比如oss << "SMEM = 0x" << std::hex << std::uppercase << vec[0];)
内容的提问来源于stack exchange,提问作者Aline Mee
相关产品推荐
相关产品推荐

