В каких случаях следует использовать список va

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

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

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

4 ответов


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

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

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

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

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


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

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

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

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

но в вариативную typesafe printf может быть реализован в C++11, как: (взято из 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");
}

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);
на ... указывает аналогичную концепцию для макросов. Но результат намного безопаснее, так как компилятор может выполнить проверку типа, и это не откладывается до выполнения.

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