【问题标题】:Calling a function with va_list argument needs va_start() at the beggining?调用带有 va_list 参数的函数需要在开头使用 va_start() 吗?
【发布时间】:2020-11-09 08:48:55
【问题描述】:

具有以下标头的函数:

int max(int n, va_list vals)

在函数内部调用:

int max_first(int n, ...)

需要在正文开始时调用va_start(vals, n)?我试过没有,它可以工作,但我不明白哪种方法是正确的。

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

【问题讨论】:

    标签: c variadic-functions c99


    【解决方案1】:

    问题是如果

    int max(int n, va_list vals)
    

    在函数内部调用:

    int max_first(int n, ...)
     
    

    需要在正文开始时调用va_start(vals, n)

    不,它没有而且它一定不能,正确的模式如下:

    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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 该函数没有必要的调用帧信息。什么意思?
      • @CătălinaSîrbu 您必须将最后一个非变量参数(max_first 中的n)传递给va_start,它将用于计算第一个可能的变量参数的地址。你的另一个函数max 没有那个变量(那个n 是不同的)。
      【解决方案3】:

      让我们从一些背景开始:根据文档,您需要在对任何 va_list 调用 va_arg 之前调用 va_start

      • “在对 va_arg 进行任何调用之前,应使用一个有效 va_list 对象 ap 的实例调用 va_start”。 (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 之前从int max_first 调用int max 完全可以吗?另外,调用两次 va_start(一次在 max 和第二次在 max_first)是一个问题?
      • 您可以在单独的段落中设置引号的格式,并在行前加上&gt;
      • @CătălinaSîrbu - 在将其传递给 max 之前,您应该在 max_first 中调用 va_start。并且您不应该在没有先致电 va_end 的情况下致电 va_start 两次。这在 C11 规范中被提及,如 stackoverflow.com/a/59608709/1366973 所述。否则它是未定义的行为。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多