【问题标题】:Trouble with va_list c++va_list c++ 的问题
【发布时间】:2016-04-07 14:04:24
【问题描述】:

我只是想编写一个带有可变数量参数的非常简单的函数,这样我就可以编写一个类似于 printf 的函数来进行赋值。查看va_list 的文档后,我不确定为什么这段代码不断给我运行时错误:

这是我的代码:

void print(string sOne , ...);
void main()
{
    print("first string", "second string", "third String");
    system("pause");
}

void print(string sOne , ...)
{
    va_list arguments;
    va_start(arguments, sOne);
     while ((va_arg(arguments, int)) != 0)
    {
        string printString = va_arg(arguments, string);
        cout << printString;
    }
    va_end(arguments);
}

【问题讨论】:

  • 请将您的运行时错误作为问题的一部分发布。
  • @RickSmith 我在我的系统上添加的错误中,它是由 va_arg 的不当使用引起的
  • 根据调用堆栈,错误发生在哪里?
  • @ivan_pozdeev 上线:string printString = va_arg(arguments, string);
  • 感谢您发布错误。请编辑您的问题以不使用您的错误图像。这将使以后更容易搜索您的问题。

标签: c++ arguments variable-assignment variadic-functions


【解决方案1】:

您对可变参数函数的实现非常不正确。

首先,您需要一种方法来告诉函数有多少参数或它们何时结束。标准 printf 通过使用格式说明符(它们的数字代表 args 的数量)来做到这一点,另一种选择是显式提供数字。您似乎期望最后一个参数是整数 0(顺便说一句奇怪的选择。),但您永远不会将 0 作为最后一个参数传递给您的可变参数函数。

其次,您不能从可变参数函数参数中便携地提取std::string。仅支持普通类型,对于字符串,您必须使用 char*std::string 不是微不足道的,因为它具有非平凡的构造函数和析构函数。一些编译器确实支持非平凡类型作为此类函数的参数,但其他编译器不支持,因此您不应该尝试这样做。

最后但并非最不重要的一点:可变参数函数在 C++ 世界中没有地位,即使是赋值也是如此。

【讨论】:

  • 我不同意你的最后陈述。 Varadic 函数对于元编程至关重要。
  • @Jonathan Mee C++ 有可变参数模板和 initializer_list,你还需要什么? va_list 只是遗留问题恕我直言。
  • @JesperJuhl,他们是。在表达 SFINAE 降临在我们身上之前。
  • 您经常需要va_list 来利用 Varadic 模板,尽管我同意在很多情况下此类封装已经成熟。
  • @JesperJuhl,我想,他指的是检查成员是否存在的几十年前的老把戏。
【解决方案2】:

SergeyA 解释了为什么您的代码不起作用,这是可能的解决方案之一:

void print(const char *sOne , ...);
int main()
{
    print("first string", "second string", "third String", nullptr);
    system("pause");
}

void print(const char *sOne , ...)
{
    va_list arguments;
    va_start(arguments, sOne);
    while (sOne)
    {
        cout << sOne;
        sOne = va_arg(arguments, const char *);
    }
    va_end(arguments);
}

同样,这个例子是如果你必须使用 C 风格的可变参数函数,你应该考虑使用 C++ 可变参数模板。

【讨论】:

    【解决方案3】:

    所以你的实现有几个问题。

    1. 您没有对va_arg 的调用次数进行限制,并且:

    如果在ap 中没有更多参数时调用va_arg,或者如果ap 中的下一个参数的类型(在提升之后)与T 不兼容,则行为未定义

    1. 您似乎想要打印“第一个字符串”,因此您需要将其作为 va_list 的一部分传递,您可以通过以下方式清除 12使用计数作为您的第一个参数:void print(int sOne, ...)
    2. 您将const char*s 作为参数传递给您的va_list,并期望它将这些参数提升为strings。 va_list 不会为您宣传。它将您传入的任何内容强制转换为va_arg 中指定的类型。如果您尝试将const char* 视为string,您最终会遇到运行时错误,如您所见。 这可以通过使用strings 作为你的va_list 参数来纠正:"first string"s "second string"s, "third String"sthis is only conditionally supported in C++你需要使用va_arg(arguments, const char*)
    3. 您正在调用va_arg 来推进您的for 循环。您需要为此使用计数器。调用va_arg 提取下一个参数,它们测试参数是否可用。而是使用您作为 2 for (auto i = 0; i &lt; sOne; ++i) 的一部分传入的计数

    进行这些更改会使您的代码看起来像这样:

    void print(int sOne, ...) {
        va_list arguments;
        va_start(arguments, sOne);
    
        for (auto i = 0; i < sOne; ++i) {
            cout << va_arg(arguments, const char*) << endl;
        }
        va_end(arguments);
    }
    

    Live Example

    【讨论】:

    • 未定义的行为。 If va_arg is called when ... the type of the next argument in ap (after promotions) is not compatible with T, the behavior is undefined...
    • @SergeyA 我同意这一点......但我假设你发布了它,因为我的答案似乎没有?您能否向我解释一下您的陈述与我所说的有何冲突?
    • 本例中参数的实际类型是const char*std::string 不是const char* 的兼容类型,并且const char* 不能提升std::string。因此va_arg(arguments, string) 运动未定义的行为。
    • @SergeyA 你在投反对票之前读过我的回答吗?密切关注3string::operator ""s
    猜你喜欢
    • 2022-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-08
    • 1970-01-01
    • 1970-01-01
    • 2010-11-02
    相关资源
    最近更新 更多