для чего именно va\_end? всегда ли это нужно называть?

<blockquote> <p><code>va\_end</code> - макрос для сброса <code>arg\_ptr</code>.</p> </blockquote><p>после доступа к списку переменных аргументов указатель <code>arg\_ptr</code> обычно сбрасывается с помощью <code>va\_end()</code>. я понимаю, что это необходимо, если вы хотите повторить список, но действительно ли это нужно, если вы не собираетесь? является ли это просто хорошей практикой, как правило "всегда иметь <code>default:</code> в вашем <code>switch</code>"?</p>

<p><code>va\_end</code> используется для очистки. вы не хотите разбить стек, не так ли?</p><p>от <code>man va\_start</code>:</p><blockquote> <p>va\_end ()</p> <p>каждому вызову va\_start () должен соответствовать соответствующий вызов va\_end () в той же функции. после вызова va\_end (ap) переменная ap не определена. возможны множественные обходы списка, каждый из которых заключен в квадратные скобки с помощью va\_start () и va\_end (). va\_end () может быть макросом или функцией.</p> </blockquote><p>обратите внимание на присутствие слова <strong>обязательно</strong>.</p><p>стек может быть поврежден, потому что <em> вы не знаете, что делает <code>va\_start()</code> </em>. макросы <code>va\_*</code> должны рассматриваться как черные ящики. каждый компилятор на любой платформе может делать там что хочет. это может ничего не делать, или это может сделать многое.</p><p>некоторые abi передают первые несколько аргументов в регистрах, а остальные в стеке. <code>va\_arg()</code> там может быть более сложным. вы можете посмотреть, как данная реализация выполняет переменные, что может быть интересно, но при написании переносимого кода вы должны рассматривать их как непрозрачные операции.</p>

<p>в linux x86-64 можно выполнить только один обход через переменную <code>va\_list</code>. чтобы сделать больше обходов, его сначала нужно скопировать, используя <code>va\_copy</code>. <code>man va\_copy</code> объясняет детали:</p><blockquote> <p>va\_copy ()</p> <p>очевидная реализация будет иметь va\_list указатель на стековый фрейм функции variadic. в такой настройке (безусловно, самая распространенная), кажется, ничего против назначения</p> <pre><code> va\_list aq = ap; </code></pre> <p>к сожалению, есть также системы, которые делают его массивом указателей (длиной 1), и здесь нужно</p> <pre><code> va\_list aq; *aq = *ap; </code></pre> <p>наконец, в системах, где аргументы передаются в регистрах, va\_start () может потребоваться выделить память, сохранить там аргументы, а также указать, какой аргумент следующий, чтобы va\_arg () могла пройти по списку. теперь va\_end () может снова освободить выделенную память. чтобы приспособиться к этой ситуации, c99 добавляет макрос va\_copy (), так что вышеуказанное назначение может быть заменено на</p> <pre><code> va\_list aq; va\_copy(aq, ap); ... va\_end(aq); </code></pre> <p>каждому вызову va\_copy () должен соответствовать соответствующий вызов va\_end () в той же функции. некоторые системы, которые не предоставляют va\_copy (), вместо этого имеют \_\_va\_copy, так как это имя использовалось в проекте предложения.</p> </blockquote>

<p>в общей реализации "параметры, передаваемые в стеке", я считаю, что va\_end () обычно ничто/пустое/нулевое. однако на платформах, которые имеют менее традиционные схемы, это становится необходимым. это "хорошая практика", чтобы включить его, чтобы оставаться нейтральным на платформе.</p>