В каких случаях следует использовать va_list - PullRequest
6 голосов
/ 08 декабря 2011

Я создал небольшую библиотеку C, которая реализует алгоритмы теории графов и связывает их для использования в Python.

Я посылаю его другу, чтобы проверить, и он сказал мне, что va_list "опасно" и не должно использоваться в подобных проектах.

Так что вопрос в том. В каких случаях следует использовать va_list?

Ответы [ 4 ]

8 голосов
/ 08 декабря 2011

Основная проблема, которую я вижу, состоит в том, что нет гарантии, что вы действительно получили количество аргументов, которое вы ожидали, и нет способа проверить это. Это делает ошибки необнаружимыми, и необнаружимые ошибки, очевидно, являются наиболее опасным видом. va_arg также не является типобезопасным, что означает, что если вы передадите double и ожидаете unsigned long long, вы получите мусор вместо красивого целого числа и не сможете обнаружить его во время компиляции , (Это становится намного более беспорядочным, когда типы даже не имеют одинаковый размер).

В зависимости от данных, с которыми вы имеете дело, это может быть более или менее проблематичным. Если вы передадите указатели, то пропустить аргумент станет практически фатальным, потому что вместо этого ваша функция будет извлекать мусор, и это может (если планеты правильно выровнены) стать уязвимостью.

Если вы передаете «обычные» числовые данные, это зависит от того, является ли функция критической. В некоторых случаях вы можете легко обнаружить ошибку, глядя на выходные данные функции, а в некоторых практических случаях это действительно не такая большая проблема, если функция не работает.

На самом деле все вращается, если вы сами боитесь забыть аргументы.

C ++ 11 имеет функцию шаблонов с переменным числом аргументов, которая позволяет безопасно обрабатывать произвольное количество параметров. Если переход с C на C ++ не причиняет слишком много вреда, вы можете посмотреть на него.

5 голосов
/ 08 декабря 2011

В C ++ 11 никогда не следует использовать va_list, поскольку он предоставляет лучшую альтернативу, называемую шаблон переменной , который безопасен при типах, тогда как va_list - нет.

В C вы можете использовать va_list, когда вам нужна переменная функция, но будьте осторожны, так как она небезопасна.

И да, ваш друг прав: va_list опасен 1014 *. Избегайте этого как можно больше.

В C и C ++ 03 стандартная библиотечная функция printf реализована с использованием va_list, поэтому программисты на C ++ 03 обычно избегают ее использования, поскольку она небезопасна.

Но в C ++ 11 может быть реализован вариант типов безопасности printf, как: (взято из wiki )

void printf(const char *s)
{
    while (*s) {
      if (*s == '%' && *(++s) != '%')
        throw std::runtime_error("invalid format string: missing arguments");
      std::cout << *s++;
    }
}

template<typename T, typename... Args>
void printf(const char *s, T value, Args... args)
{
    while (*s) {
      if (*s == '%' && *(++s) != '%') {
         std::cout << value;
         ++s;
         printf(s, args...); 
         return;
      }
      std::cout << *s++;
    }
    throw std::logic_error("extra arguments provided to printf");
}
1 голос
/ 08 декабря 2011

va_list имеет некоторые недостатки, связанные с занижением аргументов функции:

  • при вызове такой функции компилятор не знает, какие типы Ожидаются аргументы, поэтому стандарт навязывает некоторые «обычные преобразование ", прежде чем аргументы передаются в функцию. Например, все продвигаются целые числа, которые int, все float повышен до double. В некоторых случаях вы не получили то, что вы хотел в вызываемой функции.
  • В вызываемой функции вы указываете компилятору, какой тип аргумента ты ожидаешь и сколько их. Нет никаких гарантий, что звонящий все правильно понял.

Если вы в любом случае передаете число аргументов, и они относятся к одному и тому же известному типу, вы можете просто передать их с помощью временного массива, написанного для C99:

void add_vertices(graph G, vertex v, size_t n, vertex neigh[n]);

Вы бы назвали это что-то вроде этого

add_vertices(G, v, nv, (vertex []){ 3, 5, 6, 7 });

Если это соглашение о вызовах кажется вам слишком уродливым, вы можете заключить его в макрос

#define ADD_VERTICES(G, V, NV, ... ) add_vertices((G), (V), (NV), (vertex [NV]){ __VA_ARG__ })

ADD_VERTICES(G, v, nv, 3, 5, 6, 7);

здесь ... указывает на похожую концепцию для макросов. Но результат намного безопаснее, поскольку компилятор может выполнить проверку типа, и это не задерживается на выполнение.

0 голосов
/ 08 декабря 2011

Если вы хотите реализовать функцию в C с переменным числом аргументов, вы можете использовать va_list.Например, printf использует va_list.Не уверен, почему это может быть опасно.

...