2012-09-11 9 views
17
int max(int n, ...) 

Estoy usando la convención de llamada cdecl donde la persona que llama limpia la variable después de que regrese el destinatario.¿Cómo se implementan los argumentos variables en gcc?

Estoy interesado en saber cómo funcionan las macros va_end, va_start y va_arg?

¿La persona que llama pasa la dirección de la matriz de argumentos como el segundo argumento al máximo?

Respuesta

22

Si nos fijamos en la forma en que el lenguaje C almacena los parámetros en la pila, la forma en la macros de trabajo debe convertirse en claro: -

Higher memory address Last parameter 
         Penultimate parameter 
         .... 
         Second parameter 
Lower memory address  First parameter 
     StackPointer -> Return address 

(nota, dependiendo del hardware del puntero de pila tal vez uno abajo de línea y el más alto y más bajo se pueden intercambiar)

los argumentos se almacenan siempre como esto , incluso sin el tipo ... parámetro.

El va_start macro simplemente establece un puntero a la primera parámetro de función, e.g.:-

void func (int a, ...) 
{ 
    // va_start 
    char *p = (char *) &a + sizeof a; 
} 

que hace p punto para el segundo parámetro.La macro va_arg hace esto: -

void func (int a, ...) 
{ 
    // va_start 
    char *p = (char *) &a + sizeof a; 

    // va_arg 
    int i1 = *((int *)p); 
    p += sizeof (int); 

    // va_arg 
    int i2 = *((int *)p); 
    p += sizeof (int); 

    // va_arg 
    long i2 = *((long *)p); 
    p += sizeof (long); 
} 

la va_end macro simplemente establece el valor p-NULL.

NOTAS:

  1. compiladores de optimización y algunas almacenar parámetros CPU RISC de registros en lugar de utilizar la pila. La presencia del parámetro ... desactivaría esta capacidad y el compilador utilizaría la pila.
+10

Esto es realmente bastante específico de la plataforma, ya que * muchas * convenciones de llamadas (incluyendo el común x64, PPC, ARM) pasan la mayoría de sus parámetros en los registros. Muchas plataformas no colocan la dirección de retorno en la pila, una o dos plataformas tienen acumulaciones que crecen hacia arriba en lugar de hacia abajo, y algunas convenciones de llamadas colocan argumentos en la pila en el orden opuesto. –

+0

@Skizz: ¡Respuesta impresionante! – Bruce

+0

@DietrichEpp: Lo sé. Pero, con suerte, tiene algunos elementos básicos. Puse algunas notas en la respuesta para reflejar las diversas maneras en que las pilas funcionan. Aún así, cubrir la mayoría de las diferentes formas en que el compilador implementa esto tomaría una respuesta mucho más larga. La forma más sencilla sería encontrar las definiciones de macro y ver que se expanden y, con suerte, no se está produciendo una espeluznante magia de compilación. – Skizz

8

Como los argumentos se pasan en la pila, va_ "funciones" (la mayoría de las veces se implementan como macros) simplemente manipulan un puntero de pila privada. Este puntero de pila privada se almacena desde el argumento pasado a va_start, y luego va_arg "muestra" los argumentos de la "pila" mientras itera los parámetros.

digamos que se llama a la función max con tres parámetros, así:

max(a, b, c); 

Dentro de la función max, la pila básicamente tiene el siguiente aspecto:

 
     +-----+ 
     | c | 
     | b | 
     | a | 
     | ret | 
SP -> +-----+ 

SP es el puntero de pila real, , y no es realmente a, b y c que en la pila pero sus valores. ret es la dirección de retorno, a donde ir cuando se realiza la función.

Lo va_start(ap, n) hace es tomar la dirección del argumento (n en su prototipo de función) y desde que calcula la posición del siguiente argumento, por lo que obtener un nuevo puntero de pila privada:

 
     +-----+ 
     | c | 
ap -> | b | 
     | a | 
     | ret | 
SP -> +-----+ 

Cuando use va_arg(ap, int) devuelve aquello a lo que apunta el puntero de la pila privada, y luego lo "salta" cambiando el puntero de la pila privada para apuntar ahora al siguiente argumento. La pila ahora se ve así:

 
     +-----+ 
ap -> | c | 
     | b | 
     | a | 
     | ret | 
SP -> +-----+ 

Esta descripción es, por supuesto, simplificada, pero muestra el principio.

+0

Surely 'va_arg' no puede sacarlo de la pila si no se sienta una convención de limpieza de llamadas. – Puppy

+0

@Joachim: ¿Puedes dar algunas ilustraciones o describir tu respuesta con más detalle? No puedo visualizar lo que estás diciendo. – Bruce

+0

@DeadMG Por supuesto que no, es por eso que puse pops dentro de las citas. :) –

0
int max(int n, const char *msg,...) 
{ 
va_list args; 
char buffer[1024]; 
va_start(args, msg); 
nb_char_written = vsnprintf(buffer, 1024, msg, args); 
va_end(args); 
printf("(%d):%s\n",n,buffer); 
} 
+0

Gracias por su respuesta. Estoy más interesado en saber cómo se configura la pila para el destinatario (cómo se empuja) y cómo funcionan las macros. – Bruce

Cuestiones relacionadas