假设我有两个用于调试输出的C ++函数:
void Trace( const wchar_t* format, ... )
{
va_list args;
va_start( args, format );
VarArgTrace( format, args );
va_end( args );
}
void VarArgTrace( const wchar_t* format, va_list args )
{
WCHAR buffer[1024];
//use ::_vsnwprintf_s to format the string
::OutputDebugStringW( buffer );
}
以上使用的是Win32 OutputDebugStringW()
,但这并不重要。现在我想优化格式化,这样当没有调试器附加格式化时(我测量 - 加速很重要):
void Trace( const wchar_t* format, ... )
{
if( !IsDebuggerPresent() ) {
return;
}
//proceed as previously
va_list args;
.....
}
我会在IsDebuggerPresent()
返回null后提前返回的事实会影响除格式化之外的所有内容吗?
我的意思是我不再致电va_start
和va_end
- 这有关系吗?是否会跳过va_start
和va_end
导致任何意外的行为更改?
答案 0 :(得分:6)
不,没有义务在varargs函数中使用va_start。
如果您不使用va_start,则无法使用va_end;如果你使用va_start,你应该使用va_end,无论函数如何返回。
答案 1 :(得分:1)
所有这些宏(至少在Windows上)都是arg列表中书签的指针操作。早点回来会好的。这是特定于编译器的,但我无法想象为什么早期返回会在其他平台上出现问题。
来自x86 vadefs.h
:
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )
答案 2 :(得分:1)
提前退货的唯一要求是,如果您使用过(已执行)va_start()
,则必须在返回前使用va_end()
。
如果你藐视这条规则,你就可以在大多数系统上使用它,但某些系统需要va_end()
,所以不要冒险忽略它。省略它是未定义的行为。
除了这条规则外,由您决定如何处理您的回报。你建议的早期回归不是问题。
答案 3 :(得分:1)
这是非常安全的,因为在C语言中,从堆栈中“清除”传递参数的工作总是由调用函数完成,而不是被调用函数。
有些语言(Pascal想到的)可能会反过来这样做。这可能更有效,但只能起作用,因为没有可变数量的参数的概念。
答案 4 :(得分:0)
varargs中没有太多开销,通常只是几个指针操作。 为每个varargs调用设置堆栈可能会略微增加开销,因此不是
void Trace( const wchar_t* format, ... ) { if( !IsDebuggerPresent() ) { return; } //proceed as previously va_list args; ..... }
你可能最好使用预处理器来防止在非调试版本中调用跟踪内容。
使用
乱丢你的代码if( IsDebuggerPresent() ) Trace( stuff... );
似乎也非常沮丧。
VC2010获得了可变参数宏了吗? :)