2009-02-25 14 views
46

va_end - Macro para restablecer arg_ptr.¿Para qué va_end exactamente? ¿Siempre es necesario llamarlo?

Después de acceder a una lista de argumentos variable, el puntero arg_ptr por lo general se restablece con va_end(). Entiendo que es necesario si desea volver a iterar la lista, pero ¿es realmente necesario si no va a hacerlo? ¿Es solo una buena práctica, como la regla "siempre tener un default: en su switch"?

+1

Es una muy buena pregunta. Me gustaría que alguien lo respondiera describiendo una arquitectura donde va_end no es una opción negativa. – erikkallen

+1

FYI: MSVS2008 - #define _crt_va_end (ap) (ap = (va_list) 0) – Yarik

+0

@erikkallen: Haga una búsqueda en google de "define va_end" y encontrará algunas definiciones inusuales que pueden o no ser esencialmente un no- op. – PlasmaHH

Respuesta

40

va_end se utiliza para hacer la limpieza. No quieres destrozar la pila, ¿verdad?

De man va_start:

va_end()

Cada invocación de va_start() debe ser emparejado por una invocación correspondiente de va_end() en la misma función. Después de la llamada va_end (ap), la variable ap no está definida. Se pueden realizar múltiples recorridos de la lista, cada uno entre corchetes con va_start() y va_end(). va_end() puede ser una macro o una función.

Tenga en cuenta la presencia de la palabra debe.

La pila podría dañarse porque no sabe qué va_start() está haciendo. Las macros va_* están destinadas a ser tratadas como cuadros negros. Cada compilador en cada plataforma puede hacer lo que quiera allí. Puede no hacer nada, o puede hacer mucho.

Algunos ABI pasan las primeras args en los registros, y el resto en la pila. A va_arg() puede ser más complicado. Puede buscar cómo una implementación determinada hace varargs, lo que puede ser interesante, pero al escribir un código portátil debe tratarlos como operaciones opacas.

+0

¿Significa que el puntero es 'global' y cuando la función se llama por segunda vez sin restablecer el puntero, la pila se dañará? – Yarik

+3

Podría corromperse porque * usted no sabe lo que va_start() está haciendo. * Podría estar haciendo cualquier cosa. Y necesita ser limpiado. Por lo tanto, cuando llame a va_start(), * DEBE * hacer coincidirlo con va_end(). – greyfade

+0

Gracias por la aclaración. – Yarik

10

En la implementación común de "parámetros pasados ​​en la pila", creo que va_end() generalmente no es nada/vacío/nulo. Sin embargo, en plataformas que tienen esquemas menos tradicionales, se hace necesario. Es una "buena práctica" incluirlo para permanecer neutral en la plataforma.

+0

Podría reiniciar la pila en caso de que no iteraras sobre var_args. – Spidey

11

En Linux x86-64 solo se puede realizar un recorrido en una variable va_list. Para hacer más cruces, primero debe copiarse usando va_copy. man va_copy explica los detalles:

va_copy()

Una aplicación obvia tendría un va_list ser un puntero a la estructura pila de la función variadic.En una configuración de este tipo (con mucho el más común) no parece nada en contra de una asignación

va_list aq = ap; 

Por desgracia, también hay sistemas que lo convierten en un vector de punteros (de longitud 1), y por tanto es necesario

va_list aq; 
    *aq = *ap; 

Finalmente, en sistemas donde se pasan argumentos en registros, puede ser necesario para va_start() para asignar memoria, almacenar los argumentos allí, y también una indicación de que el argumento es el siguiente, de modo que va_arg () puede paso a través de la lista. Ahora va_end() puede liberar nuevamente la memoria asignada . Para adaptarse a esta situación, C99 agrega un va_copy macro(), por lo que la asignación de arriba puede ser sustituido por

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

Cada invocación de va_copy() debe ser igualada por un correspondiente ción invocación de va_end() en la misma función. Algunos sistemas que no suministran va_copy() tienen __va_copy en su lugar, ya que ese era el nombre utilizado en la propuesta de borrador .

+2

Para confundir esto; nada es especial para Linux x86-64. 'va_copy' es obligatorio si desea iterar dos veces sobre una lista cuando solo tiene disponible la variable' va_list'. (por ejemplo, dentro de una función que toma 'va_list' como argumento). Siempre puede llamar a 'va_start' y' va_end' tanto como desee. –

+0

@MattMcNabb En x86-64 'va_list' mantiene el estado de recorrido. En x86 no. –

+0

@downvoter ¿qué pasa? –