调用带有 va_list 参数的函数需要在开头使用 va_start() 吗?

Calling a function with va_list argument needs va_start() at the beggining?

具有以下 header 的函数:

int max(int n, va_list vals)

函数内部调用:

int max_first(int n, ...)

需要在 body 的开头进行 va_start(vals, n) 调用?我试过没有,但它有效,但我不明白哪种方法是正确的。

int max(int n, va_list vals)
{
    va_start(vals, n);
    // etc
}

让我们从一些背景开始:根据文档,在任何 va_list:

上调用 va_arg 之前,您需要先调用 va_start
  • "va_start 应该在对 va_arg" 的任何调用之前使用有效 va_list 对象 ap 的实例调用。 (Source)

  • “在调用 va_arg 之前,ap 必须通过调用 va_start 或 va_copy 进行初始化,中间不调用 va_end”。 (Source)

我认为不调用 va_start 未定义的行为,但我找不到具体的标注来说明这一点。

在您的问题中,函数 int max(int n, va_list vals) 不是“真正的”可变参数,因为它接受固定数量的参数:2。它们是 int nva_list vals

根据 documentation“可变参数函数的声明使用省略号作为最后一个参数,例如 int printf(const char* format, ...);

因此,这取决于您如何实现它,但我建议记录 int max(int n, va_list vals) 以接受已使用 va_start 调用初始化的 va_list。理由是它在技术上不是“可变的”并且并不真正“拥有”va_list。它只是接受它作为来自其他来源的输入。

实际的可变参数函数 int max_first(int n, ...) 应该是创建 va_list 并在将其传递到任何地方之前调用 va_start 启动它的函数。

不过,据我所知,没有办法检查 va_list 是否已经调用了 va_start。并且不能保证它会在传递给您的函数之前被调用,所以我怀疑这必须通过文档和约定来强制执行。

您必须在 max_first 中用 va_start 初始化 va_list。但是你不能在max中重做它,因为那个函数没有必要的调用帧信息。

问题是

int max(int n, va_list vals)

Called inside the function:

int max_first(int n, ...)
 

Needs a va_start(vals, n) invocation at the body's beginning?

不,不会,而且一定不会,正确的格式如下:

int max_first(int n, ...) {
    va_list vals;
    va_start(vals, n);
    int rv = max(n, vals);
    va_end(vals);
    return rv;
}

然后

int max(int n, va_list vals) {
    for (int i = 0; i < n; i ++) {
        int val = va_arg(vals, int);
        ...
    }

    ...
}

即您只能在具有 ... 的函数中调用 va_start 并且您需要在 ... 之前传入参数,并且每次调用 va_start 之后必须始终跟随着va_end 用于相同的值,如果将它传递给一个函数,则必须在之后立即调用 va_end,而不是在 调用函数 中使用它;如果您想再次处理参数,则必须 然后 再次调用 va_start