it-swarm.com.ru

Для чего именно va_end? Всегда ли это нужно называть?

va_end - Макрос для сброса arg_ptr.

После доступа к списку переменных аргументов указатель arg_ptr обычно сбрасывается с помощью va_end(). Я понимаю, что это необходимо, если вы хотите повторить список, но действительно ли это нужно, если вы не собираетесь? Является ли это просто хорошей практикой, как правило "всегда иметь default: в вашем switch"?

52
Yarik

va_end используется для очистки. Вы не хотите разбить стек, не так ли?

От man va_start:

va_end ()

Каждому вызову va_start () должен соответствовать соответствующий вызов va_end () в той же функции. После вызова va_end (ap) переменная ap не определена. Возможны множественные обходы списка, каждый из которых заключен в квадратные скобки с помощью va_start () и va_end (). va_end () может быть макросом или функцией.

Обратите внимание на присутствие Слова обязательно.

Стек может быть поврежден, потому что вы не знаете, что делает va_start(). Макросы va_* должны рассматриваться как черные ящики. Каждый компилятор на любой платформе может делать там что хочет. Это может ничего не делать, или это может сделать многое.

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

45
greyfade

В Linux x86-64 можно выполнить только один обход через переменную va_list. Чтобы сделать больше обходов, его сначала нужно скопировать, используя va_copy. man va_copy объясняет детали:

va_copy ()

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

   va_list aq = ap;

К сожалению, есть также системы, которые делают его массивом указателей (длиной 1), и здесь нужно

   va_list aq;
   *aq = *ap;

Наконец, в системах, где аргументы передаются в регистрах, va_start () может потребоваться выделить память, сохранить там аргументы, а также указать, какой аргумент следующий, чтобы va_arg () могла пройти по списку. Теперь va_end () может снова освободить выделенную память. Чтобы приспособиться к этой ситуации, C99 добавляет макрос va_copy (), так что вышеуказанное назначение может быть заменено на

   va_list aq;
   va_copy(aq, ap);
   ...
   va_end(aq);

Каждому вызову va_copy () должен соответствовать соответствующий вызов va_end () в той же функции. Некоторые системы, которые не предоставляют va_copy (), вместо этого имеют __va_copy, так как это имя использовалось в проекте предложения.

13
Maxim Egorushkin

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

12
James Curran