va_list 传递给使用 va_arg 的函数无效

va_list passed to a function using va_arg is not working

我有这段使用可变参数函数的代码:

#include <cstdarg>
#include <iostream>

template <typename T>
void bar(va_list vl) {
    std::cout << va_arg(vl, T) << std::endl;
}

void foo(int n, ...) {
    va_list vl;
    va_start(vl, n);
    while (n--) {
        bar<int>(vl);
    }
    va_end(vl);
}

int main() {
    foo(3, 1, 2, 3);
    return 0;
}

不幸的是,这段代码的输出依赖于平台。在 GCC 7 中,我得到:

1
2
3

而在 MSVC 2015 中是

1
1
1

我正在尝试编写代码来获取 GCC 输出,但我想我做错了什么,我不知道在哪里。如果有的话,哪个是正确的输出?

两个编译器都设置为最高警告级别,但它们没有打印任何警告。 在 MSVC 中,作为 va_list char* 的别名,输出是有意义的。在 GCC 中,它是一个 builtin type,我不知道那里发生了什么。

编辑:

如果我将 bar 的定义更改为 void bar(va_list& vl),则输出是相同的。通过引用传递 va_list 是否合法?

您的程序调用了未定义的行为。

semantics of C standard library in C++ is the same as in C and the content of cstdarg should be the same as stdarg.h in C. C++ standard is big, so I'll use C standard here, which is easier for me to read. From C99 7.15.3,注释我的:

.... The object ap [of type va_list, my annotation] may be passed as an argument to another function; if that function invokes the va_arg macro with parameter ap, the value of ap in the calling function is indeterminate and shall be passed to the va_end macro prior to any further reference to ap.

如果您将 va_list 对象传递给另一个函数并在该函数中调用 va_arg,则必须在同一函数中调用 va_end

根据footnote 221 from C99你可以传递一个指向va_list的指针。

您应该使用现代 C++11 方法。使用老式的 C 可变参数函数是危险的。

您可以使用 C++ 模板参数包,但在您的情况下简单 std::initializer_list 就可以完成工作并且非常方便:

void foo(std::initializer_list<int> items) {
    for (auto item : items) {
        std::cout << item  << std::endl;
    }
}

int main() {
    foo({1, 2, 3});

    return 0;
}

demo