c - 重用可变参数

标签 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;
}

现在我的问题是:如何重启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/

相关文章:

c - 如何理解这个用于 PowerPC stwbrx 的 GNU C 内联汇编宏

C可变包装器

具有 move 语义的 C++ 可变工厂导致运行时崩溃

c++ - 生成模板包

c++ - OpenCV OpenGLDrawCallback 没有被调用

c++ - 在 PostgreSQL C 扩展中包含头文件

C LUA API - 获取索引处的表值

c - GSM SIM900A 模块与 PIC18F4520 Controller 连接

c++ - 使用可变参数模板作为类和方法的参数

Cocoa - 从另一个可变参数方法调用可变参数方法(NSString stringWithFormat 调用)