2016-12-15 76 views
0

在实现一个简单的printf样式记录器时,我遇到了一个vsnprintf崩溃。这就是我所说的记录器实用程序:简单记录器类vsnprintf崩溃与读取访问冲突

LoggerUtil->LogInfo("Whatever info here %s", "just a test!"); 

它调用具有可变数量参数的函数。我们的想法是多余的信息添加到格式字符串,所以我需要改变FMT:

std::string LoggerUtil::LogClientInfo(const char* fmt) 
{ 
    return "Some info here %s"; 
} 

void LoggerUtil::LogInfo(const char* fmt, ...) 
{ 
    std::string formatStr = LogClientInfo(fmt); // returns "Some info here %s" just for testing altering the format string 
    const char* format = formatStr.c_str(); // checked memory and its '\0' terminated string 
    va_list arg_list; 
    va_start(arg_list, format); 
    Logger::InfoVA(format, arg_list); 
    va_end(arg_list); 
} 

void Logger::InfoVA(const char* fmt, va_list arg_list) 
{ 
    Log(Priority_Info, fmt, arg_list); 
} 

void Logger::Log(Priority priority, const char* fmt, va_list args) 
{ 
    char str[MaxLogEntrySize]; 
    memset(str,0,MaxLogEntrySize*sizeof(char)); 
    vsnprintf(str,MaxLogEntrySize-1, fmt, args); // CRASH :(
    ... 
} 

想不通的主要问题,而不是与FMT回火解决了这个问题,但它不是一个选项:

void LoggerUtil::LogInfo(const char* fmt, ...) 
{ 
    va_list arg_list; 
    va_start(arg_list, fmt); 
    Logger::InfoVA(fmt, arg_list); 
    va_end(arg_list); 
} 

我在这里错过了什么?

+1

我没有立即看到一个问题。使用内存检测工具(如purify或valgrind)来搜索内存损坏。仅仅因为一个C++程序崩溃在一个特定的功能并不意味着这是错误的地方。 –

+0

'va_start(arg_list,format);''我想它应该是'va_start(arg_list,fmt);''除非你试图做一些棘手的事情,我无法得到。但基本上你在其他地方兜售'va_start',UB。我没有发布这个答案,因为我不确定它是否修复了代码中的所有可能的问题。 –

+0

由于您使用'C++',因此有更好的方法来实现可变数量的参数,例如使用[变量参数模板](http://en.cppreference.com/w/cpp/language/parameter_pack)。然后,你不会与va_start,va_arg等混淆。请参阅链接中的示例代码。 – PaulMcKenzie

回答

2

使用LogClientInfo()创建新格式字符串后,您将错误的输入值传递给第二个参数va_start()。您正在传递您的本地format变量,但您需要传递LogInfo()的参数fmt。仅当因调用Logger::InfoVA()而使用其他格式字符串时,不会更改输入格式值的存储位置。 va_start()配置va_list在接下来的功能相对于指定的参数的参数点,在这种情况下,需要为fmt函数参数,而不是本地format变量:

void LoggerUtil::LogInfo(const char* fmt, ...) 
{ 
    std::string formatStr = LogClientInfo(fmt); 
    va_list arg_list; 
    va_start(arg_list, fmt); // <-- use fmt here ! 
    Logger::InfoVA(formatStr.c_str(), arg_list); 
    va_end(arg_list); 
} 
+0

我非常感谢你,改变了va_start(arg_list,format);到va_start(arg_list,fmt);解决了我的问题。非常感谢你!无论如何,我正在使用C++ 14,所以我会用variadic模板重写所有内容。不想用这些反直觉的va_list再次拍摄自己的脚。 – CrHasher