【问题标题】:Pass va_list or pointer to va_list?传递 va_list 或指向 va_list 的指针?
【发布时间】:2010-07-30 07:53:10
【问题描述】:

假设我有一个函数,它接受可变参数 (...) 或从另一个此类函数传递的 va_list。主要逻辑在这个函数本身(我们称之为f1),但我想让它把va_list传递给另一个函数(我们称之为f2),它将确定下一个参数类型,获取它使用va_arg,并正确转换并存储以供调用者使用。

将va_list 传递给f2 是否足够,或者是否有必要将指针传递给va_list。除非要求 va_list 是数组类型,否则将其位置数据存储在 va_list 对象指向的位置(而不是对象本身),我看不出按值传递它如何允许调用函数( f1) 来“查看”va_arg 调用的函数所做的更改。

有人能解释一下吗?我对标准的要求感兴趣,而不是某些特定实现允许的内容。

【问题讨论】:

    标签: c


    【解决方案1】:

    看起来你需要传递一个指向 va_list 的指针。有关详细信息,请参阅 C99 standard document 第 7.15 节。特别是,要点 3 指出:

    对象 ap 可以作为参数传递给 另一个功能;如果该函数调用带有参数 ap 的 va_arg 宏, 调用函数中 ap 的值是不确定的,应该传递给 va_end 在进一步引用应用程序之前的宏

    [我的斜体]

    编辑:刚刚注意到标准中的一个脚注:

    215) 允许创建指向 va_list 的指针并将该指针传递给另一个函数,其中 如果其他函数返回后,原始函数可能会进一步使用原始列表

    所以你可以传递一个指向 va_list 的指针并在被调用的函数中执行va_arg(*va_list_pointer)

    【讨论】:

    • 同意您的上次编辑 - 指针是要走的路(因为 OP 希望对 va_list 的更改在调用函数中绝对可见)。
    • @caf:我认为你不应该删掉我对vfprintf() 的引用,因为看起来其他人也犯了和我一样的错误,并建议提问者查看现有原型(这实际上并没有回答他的问题)。
    • 我会说留下评论和/或投票否决他们 - 最好让正确的答案不被早期的错误假设混淆......
    【解决方案2】:

    据我了解,您应该直接传递 va_list(而不是指向它的指针)。 comp.lang.c 似乎支持这一点:

    “va_list 本身并不是一个可变参数列表;它实际上是一种指向一个变量的指针。也就是说,接受 va_list 的函数本身不是可变参数,反之亦然。”

    【讨论】:

    • 我不认为这句话支持你的论点。没有任何内容不允许传递指向va_list 的指针,而且标准确实明确表示这是允许的。
    【解决方案3】:

    我发现关于这个问题的文字很模棱两可。最简单的可能是在标准中查看带有va_list 的预定义函数应该如何接收它,例如vsnprintf。这显然是价值而不是参考。

    【讨论】:

    • 标准函数不打算以 OP 想要的方式调用 - 在它们被调用后,va_list 不能再使用,只能传递给va_end()。跨度>
    • 确实,我是 OP,我同意 caf 的观点。 JeremyP 是唯一一个似乎引用了任何证据的人,标准非常明确,您必须为我想要的语义传递一个指针。
    【解决方案4】:

    如果你想在子函数中使用它,你应该传递一个指向va_list的指针,然后不必立即将它传递给va_end。来自 C99:

    允许创建指向va_list 的指针并将该指针传递给另一个函数,在这种情况下,原始函数可以在其他函数返回后进一步使用原始列表。

    标准允许这样做,但是,在 va_list 是数组类型的某些 64 位平台上,这不起作用。由于数组的地址与数组的第一个元素的地址相同,因此将指向va_list 的指针传递给va_arg 时会出现段错误,并将指向va_list 的指针作为参数。

    解决此问题的一种方法是接收va_list 作为非常规的参数名称(通常带有一个或多个下划线后缀),然后创建一个新的本地va_list,如下所示:

    #include <stdarg.h>
    
    int vfoo(va_list ap_)
    {
        int ret;
        va_list ap;
        va_copy(ap, ap_);
        ret = vbar(&ap);
        /* do other stuff with ap */
        va_end(ap);
        return ret;
    }
    

    这是我在 vsnprintf 实现中使用的方法,用于从中调用其他函数进行格式化。

    【讨论】:

      【解决方案5】:

      标准 C 库中的函数传递 va_list 元素本身 (man 3 vprintf):

         #include <stdarg.h>
      
         int vprintf(const char *format, va_list ap);
         int vfprintf(FILE *stream, const char *format, va_list ap);
         int vsprintf(char *str, const char *format, va_list ap);
         int vsnprintf(char *str, size_t size, const char *format, va_list ap);
      

      【讨论】:

      • 这些函数都不打算以 OP 想要的方式调用 - 调用这些函数后,va_list 不能再使用,只能传递给 va_end()
      【解决方案6】:

      将指针传递给 va_list 在 32 位系统中可以正常工作。您甚至可以在子例程中一次获取一个参数。 但它似乎在 64 位系统中不起作用,会在 va_arg() 处产生段错误。

      【讨论】:

        猜你喜欢
        • 2012-04-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-14
        • 1970-01-01
        • 2020-03-12
        相关资源
        最近更新 更多