在Unix(Clang 3.8.1)上,此代码输出:
6:32
8:a8e
在Windows(MSVC 19.00.24215.1)上,此代码输出:
6:12345
6:a12345e
#include <iostream>
#include <stdarg.h>
static std::string getFormattedString(const char* fmt, va_list ap) {
int count = vsnprintf(NULL, 0, fmt, ap) + 1;
std::cout << count << ": ";
if (count <= 0) { return "unable to format message"; }
std::string result = std::string(count, '\0');
if (vsnprintf(&result[0], count, fmt, ap) < 0) { return "error";}
return result;
}
static std::string getFormattedString(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
std::string result = getFormattedString(fmt, ap);
va_end(ap);
return result;
}
int main(int argc, char *argv[]) {
std::cout << getFormattedString("%d", 12345).c_str() << "\n";
std::cout << getFormattedString("a%de", 12345).c_str() << "\n";
return 0;
}
有趣的是,它们都得到了正确的计数,但在我的Linux和OS X机器上,此代码输出的结果不正确。是什么导致了这个?我在某处招致了UB吗?
答案 0 :(得分:3)
正如@RaymondChen在评论中所说,vsnprintf修改ap。如果要重用va_list,则必须使用va_copy创建副本:
static std::string getFormattedString(const char* fmt, va_list ap) {
va_list ap2;
va_copy(ap2, ap);
int count = vsnprintf(NULL, 0, fmt, ap) + 1;
std::cout << count << ": ";
if (count <= 0) { return "unable to format message"; }
std::string result = std::string(count, '\0');
if (vsnprintf(&result[0], count, fmt, ap2) < 0) { return "error";}
std::cout << result.size() << ' ' << strlen(result.c_str()) << '\n';
return result;
}
这将使用原始列表两次,并产生正确的结果。