重用可变参数

Tha*_*gon 7 c variadic

我有一个关于重新启动可变参数列表(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;
}
Run Code Online (Sandbox Code Playgroud)

现在我的问题是:如何重启ap

请注意,这个问题与C++无关,但是C.


到目前为止,我找到了两种可能的解决方案,但我想知道哪一种是"正确的"或"最佳实践".

解决方案1:多重 va_start()

使用GCC7我可以替换线路

// restart ap
Run Code Online (Sandbox Code Playgroud)

在上面的例子中

va_end(ap);
va_start(fmt, ap);
Run Code Online (Sandbox Code Playgroud)

重置ap为第一个参数.但是,我不确定这是否真的是有效的代码,或者我很幸运,一些未定义的行为没有破坏结果.

解决方案2: va_copy()

另一个适用于GCC7的解决方案是初始化几个ap使用va_copy()类似的副本

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;
}
Run Code Online (Sandbox Code Playgroud)

这是有效的代码(imo),但由于现在有多个va_list实例需要复制,因此效率远低于第一个解决方案.


那么哪种解决方案最好?它是我上面提到的两个中的一个,还是完全不同的东西?

Ser*_*sta 5

va_copy方法是有效的:这恰恰是va_copy被造的。您说它的效率比第一种解决方案低得多。我不同意。首先,它是一个实现细节,但是可变参数列表通常在C中作为参数堆栈中的指针实现,指向要由检索的下一个参数va_arg。因此,va_copy它不会复制参数列表,而只是一个指针。

但是重新启动列表va_arg也是有效的。C11的n1570草案在7.16.1.3中说va_end宏(强调我的):

... va_end宏可以修改ap,使其不再可用(无需由va_start或va_copy宏重新初始化)。

我的理解是合法的是,用第一个va_arg之后的新名称重新初始化可变参数列表的处理va_end

两种方式之间的区别在于,va_copy允许同一列表的并发视图,而va_start只允许顺序视图的重新初始化(第一个关闭,第二个打开)。

我的观点是选择的标准不应该是性能,因为va_copy在体面的实现中,其开销应该可以忽略不计,但是您的实际要求是:如果您一次只希望在列表上查看一个视图va_arg,则在并发列表允许的情况下坚持重新初始化。更简单的处理方法,请在的帮助下使用va_copy