我有一个关于重新启动可变参数列表 (va_list
) 的问题。
基本上我想做这样的事情:
void someFunc(char* fmt, ...) {
va_list ap;
va_start(fmt, ap);
otherFuncA(fmt, ap);
// restart ap
otherFuncB(fmt, ap);
// restart ap
...
va_end(ap);
return;
}
现在我的问题是:如何重启ap
?
请注意,这个问题与 C++ 无关,而是与 C 相关。
到目前为止,我找到了两种可能的解决方案,但我想知道哪一种是“正确”或“最佳做法”。
方案一:多次va_start()
使用 GCC7 我可以替换行
// restart ap
在上面的例子中
va_end(ap);
va_start(fmt, ap);
将 ap
重置为第一个参数。
但是,我不确定这是否是真正有效的代码,或者我很幸运一些未定义的行为没有破坏结果。
方案二:va_copy()
另一种适用于 GCC7 的解决方案是使用 va_copy()
初始化 ap
的多个副本,例如
void someFunc(char* fmt, ...) {
va_list ap1, ap2;
va_start(fmt, ap1);
va_copy(ap2, ap1);
otherFuncA(fmt, ap1);
otherFuncB(fmt, ap2);
va_end(ap1);
va_end(ap2);
return;
}
这是有效代码 (imo),但由于现在有多个 va_list
实例需要复制,因此它的效率比第一个解决方案低得多。
那么哪种解决方案最好?它是我上面提到的两个之一,还是完全不同的东西?
最佳答案
va_copy
方法是有效的:这正是 va_copy
的用途。你说它比第一个解决方案效率低得多。我不太同意。首先它是一个实现细节,但可变参数列表通常在 C 中实现为参数堆栈中的指针,指向下一个要由 va_arg
检索的参数。所以 va_copy
不会复制参数列表,而只是一个指针。
但是通过 va_arg
重新启动列表也是有效的。 C11 的 n1570 草案在 7.16.1.3 中说 va_end 宏(强调我的):
... The va_end macro may modify ap so that it is no longer usable (without being reinitialized by the va_start or va_copy macro).
我的理解是,在第一个 va_end
之后使用新的 va_arg
重新初始化可变参数列表的处理是合法的。
这两种方式的区别在于,va_copy
允许并发查看同一列表,而使用 va_start
重新初始化只允许顺序查看(第一个在第二个打开之前关闭) ).
我的意见是,选择的标准不应该是性能,因为 va_copy
的开销在体面的实现中应该可以忽略不计,但你的真正需求是:如果你一次只想要一个 View 列表,坚持 va_arg
重新初始化,如果并发列表允许更简单的处理,请在 va_copy
的帮助下使用它。
关于c - 重用可变参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50216191/