va_list, переданный в функцию с помощью va_arg, не работает - PullRequest
0 голосов
/ 03 февраля 2020

У меня есть этот код, который играет с функциями variadi c:

#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;
}

К сожалению, вывод этого кода зависит от платформы. В G CC 7 я получаю:

1
2
3

, тогда как в MSV C 2015 это

1
1
1

Я пытаюсь написать код для получения вывода G CC , но я полагаю, что я делаю что-то не так, и я понятия не имею, где. Какой правильный вывод, если есть?

Оба компилятора установлены на максимальный уровень предупреждения, но они не выводят никаких предупреждений. В MSV C, являющемся va_list псевдонимом char*, вывод имеет смысл. В G CC это встроенный тип , и я понятия не имею, что там происходит.

Редактировать:

Если я изменю определение от bar до void bar(va_list& vl), тогда выходы такие же. Законно ли передавать va_list по ссылке?

Ответы [ 2 ]

3 голосов
/ 03 февраля 2020

Ваша программа вызывает неопределенное поведение.

Семантика стандартной библиотеки C в C ++ такая же, как в C, а содержимое cstdarg должно быть таким же, как stdarg.h в C. Стандарт C ++ большой, поэтому я буду использовать здесь стандарт C, который мне легче читать. Начиная с C99 7.15.3 , моя пометка:

.... Объект ap [типа va_list, моя аннотация] может передаваться в качестве аргумента другой функции; если эта функция вызывает макрос va_arg с параметром ap, значение ap в вызывающей функции является неопределенным и должно быть передано макросу va_end до любой дальнейшей ссылки на ap.

Если вы передадите va_list возражает против другой функции, и в этой функции, которую вы вызываете va_arg, вы должны вызывать va_end в той же функции.

Согласно сноске 221 из C99 вы можете передать указатель на va_list.

0 голосов
/ 03 февраля 2020

Вы должны использовать современный подход C ++ 11. Использование старой моды C variadi 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;
}

демо

...