8

我正在使用va_args在 C++ 中试验可变参数。这个想法很有用,而且确实是我在 C# 中通过 params 功能使用的很多东西。让我感到沮丧的一件事是上面关于 va_args 的以下摘录:

另请注意, va_arg 无法确定检索到的参数是否是传递给函数的最后一个参数(或者即使它是超出该列表末尾的元素)。

我很难相信没有办法以编程方式确定从该函数本身传递给该函数的变量参数的数量。我想执行以下操作:

void fcn(int arg1 ...)
{
    va_list argList;
    va_start(argList, arg1);

    int numRemainingParams = //function that returns number of remaining parameters
    for (int i=0; i<numRemainingParams; ++i)
    {
        //do stuff with params
    }
    va_end(argList);
}

重申一下,上面的文档表明 va_arg 不能确定检索到的 arg 是否是列表中的最后一个。但我觉得这些信息必须以某种方式访问​​。

有没有实现这一目标的标准方法?

4

5 回答 5

10

我很难相信没有办法以编程方式确定从该函数本身传递给该函数的变量参数的数量。

尽管如此,这是真的。C/C++ 不会在参数列表的末尾放置标记,所以被调用的函数真的不知道它正在接收多少个参数。如果您需要标记参数的结尾,您必须自己通过在列表末尾放置某种标记来完成。

被调用的函数也不知道所提供参数的类型或大小。这就是为什么printf和朋友们强迫您指定要插入格式字符串的值的精确数据类型,以及您可以通过使用错误格式字符串调用 printf 来使程序崩溃的原因。

请注意,参数传递由特定平台的 ABI 指定,而不是由 C++/C 标准指定。但是,ABI 必须允许 C++/C 标准可实施。例如,ABI 可能希望在寄存器中传递参数以提高效率,但在这种情况下可能无法轻松实现 va_args。因此,参数也可能在堆栈上被隐藏。但是,几乎在任何情况下都不会标记堆栈以显示参数列表的结尾,因为 C++/C 标准不要求提供此信息,因此这将是不必要的开销。

于 2012-10-15T18:02:57.250 回答
6

变量参数在 C 和 C++ 中的工作方式相对简单:参数只是被压入堆栈,被调用者有责任在一定程度上弄清楚有哪些参数。标准中没有任何内容提供确定参数数量的方法。结果,参数的数量由一些上下文信息确定,例如,格式字符串中引用的元素的数量。

个别编译器可能知道有多少元素,但没有标准接口来获取此值。

但是,您可以做的是使用可变参数模板:您可以确定有关传递给函数的参数的非常详细的信息。接口看起来不同,可能需要将参数引导到某种数据结构中,但从好的方面来说,它也适用于无法使用可变参数传递的类型。

于 2012-10-15T18:04:51.820 回答
3

不,没有。这就是变量参数不安全的原因。它们是 C 的一部分,它缺乏实现“方便”可变参数函数的类型安全的表达能力。您必须接受这样一个事实,即 C 包含的结构的正确性取决于而不仅仅是类型。这就是为什么它是一种“不安全的语言”。

不要在 C++ 中使用可变参数。它是一种更强大的语言,可让您编写同样方便且安全的代码。

于 2012-10-15T18:02:37.453 回答
1

不,没有这种方法。如果您有这样的需求,最好将这些函数参数打包到一个std::vector可以迭代的或类似的集合中。

于 2012-10-15T18:03:24.890 回答
1

变量参数列表是继承自 C++ 的 C 历史的一个非常古老的概念。它可以追溯到 C 程序员通常考虑生成的汇编代码的时代。

那时,编译器根本没有检查您在调用函数时传递给函数的数据是否与函数预期接收的数据类型匹配。做正确的事是程序员的责任。例如,如果调用者使用 a 调用函数char并且函数期望 aint程序崩溃,尽管编译器没有抱怨。

今天的类型检查可以防止这些错误,但是使用可变参数列表,您可以回到那些旧概念,包括所有风险。所以,如果你能以某种方式避免它,就不要使用它。

这个概念已有几十年历史的事实可能是与现代安全代码概念相比感觉错误的原因。

于 2012-10-15T18:38:31.207 回答