为什么va_start会失败?

时间:2014-02-12 22:04:26

标签: c++ windows macros

我想创建一些日志记录,然后我创建了一个类。但是我将参数传递给它有一些问题。

班级:

namespace debug
{
    class log
    {
        private:    // Members
            const std::string context;
            int Type;
        public:     // Methods
            void message( int Type, const std::string& message, ... );

        public:     // Constructor, Destructor
            log( const std::string& context, int Type );
            ~log();
    };//class log
}//namespace debug     

namespace debug
{
    void log::message( int Type, const std::string& message, ... )
    {
        va_list args;
        int len;

        char    *buffer;

        va_start( args, message );

        len = _vscprintf( message.c_str(), args ) + 1; // _vscprintf doesn't count terminating '\0'
        buffer = ( char* )malloc( len * sizeof( char ) );

        vsprintf_s( buffer, len, message.c_str(), args );

        va_end( args );

    }//log::message
}//namespace debug

我定义了两个宏:

#define DEBUG_METHOD( Type )  debug::log _debugLog( __FUNCTION__, Type );
#define DEBUG_MESSAGE( Type, debug_message, ... ) { _debugLog.message( Type, debug_message, ##__VA_ARGS__ ); }

我在这样的函数中使用它们:

BOOL test( BOOL *bTestVal)
{
   DEBUG_METHOD( INFO );
   DEBUG_MESSAGE( INFO, "Argument1 = '%s'", BoolToString( ( BOOL )*bTestVal) );

       //here comes some work...
}

不幸的是我总是收到错误。此行len = _vscprintf( message.c_str(), args ) + 1;始终抛出错误。我认为va_start导致此问题,因为args的值为+ args 0x0052eed8 "ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ... char *

有人可以帮我解决我的错误吗?

提前致谢!

3 个答案:

答案 0 :(得分:6)

  

18.10 / 3 ...参数parmN是函数定义的变量参数列表中最右边的参数的标识符(恰好在......之前)。如果使用函数,数组或引用类型声明参数parmN,或者使用与传递没有参数的参数时产生的类型不兼容的类型,行为未定义

强调我的。

答案 1 :(得分:3)

Igor已经为您提供了正式引用,这里是一个代码片段,用Visual C ++显示确切的结果。问题是va_start需要将指针前进以跳过它开始的参数,并且虽然引用有效地作为指针传递,但跳过大小被视为参数的完整大小。这使指针远远超出预期。

使用va_startA*A&进行比较:

class A
{
public:
    char B[1024];
};

void C(int f, A* a, ...)
{
    va_list Arguments;
    va_start(Arguments, a);
    int d = va_arg(Arguments, int);
    _tprintf(_T("0x%p, 0x%p, %d, %d\n"), &f, Arguments, (char*) &f - (char*) Arguments, d);
}

void E(int f, A& a, ...)
{
    va_list Arguments;
    va_start(Arguments, a);
    int d = va_arg(Arguments, int);
    _tprintf(_T("0x%p, 0x%p, %d, %d\n"), &f, Arguments, (char*) &f - (char*) Arguments, d);
}

int _tmain(int argc, _TCHAR* argv[])
{
    A a;
    C(0, &a, 1234);
    E(0, a, 1234);

你得到的输出是这样的:

0x0018F9E0, 0x0018F9EC, -12, 1234
0x0018F9E0, 0x0018FEC0, -1248, -858993460

两个函数的堆栈帧都在相同的地址(预期)开始,然后va_start的结果将指针移动不同的距离,这与A类的大小相关(12对1248)。

这就是为什么你不能将va_start与引用类型的参数一起使用:它无法正确跳过变量并开始在适当的内存位置处理你的省略号。

答案 2 :(得分:2)

使用const char *作为省略号之前的参数。正如在另一个答案中指出的那样,行为是未定义的。

此外,看起来你的log :: message函数有内存泄漏。你在不调用free()的情况下使用malloc(),并且在返回malloc()时也没有检查NULL(为什么你在C ++程序中使用malloc())