todas tipos lenguaje las funciones ejemplos c++ c variadic-functions

c++ - las - tipos de funciones en lenguaje c



Técnicamente, ¿cómo funcionan las funciones variadas? ¿Cómo funciona Printf? (2)

El estándar C y C ++ no tiene ningún requisito sobre cómo debe funcionar. Un compilador compatible puede decidir emitir listas encadenadas, std::stack<boost::any> o incluso polvo de pony mágico (según Xeo) bajo el capó.

Sin embargo, por lo general se implementa de la siguiente manera, aunque las transformaciones como argumentos en línea o pasadas en los registros de la CPU no pueden dejar nada del código discutido.

Tenga en cuenta también que esta respuesta describe específicamente una pila creciente en los gráficos siguientes; Además, esta respuesta es una simplificación solo para demostrar el esquema (consulte https://en.wikipedia.org/wiki/Stack_frame ).

¿Cómo se puede invocar una función con un número no fijo de argumentos?

Esto es posible porque la arquitectura subyacente de la máquina tiene una llamada "pila" para cada hilo. La pila se usa para pasar argumentos a las funciones. Por ejemplo, cuando tienes:

foobar("%d%d%d", 3,2,1);

Entonces esto compila a un código ensamblador como este (ejemplar y esquemáticamente, el código real puede verse diferente); tenga en cuenta que los argumentos se pasan de derecha a izquierda:

push 1 push 2 push 3 push "%d%d%d" call foobar

Esas operaciones de empuje llenan la pila:

[] // empty stack ------------------------------- push 1: [1] ------------------------------- push 2: [1] [2] ------------------------------- push 3: [1] [2] [3] // there is now 1, 2, 3 in the stack ------------------------------- push "%d%d%d":[1] [2] [3] ["%d%d%d"] ------------------------------- call foobar ... // foobar uses the same stack!

El elemento de la pila inferior se denomina "Parte superior de la pila", a menudo abreviado "TOS".

La función foobar ahora accedería a la pila, comenzando en el TOS, es decir, la cadena de formato, que como recordarán fue presionada al final. Imagine que stack es su puntero de pila, stack[0] es el valor en los TOS, stack[1] es uno por encima de los TOS, y así sucesivamente:

format_string <- stack[0]

... y luego analiza la cadena de formato. Mientras analiza, reconoce las %d tokens, y para cada una, carga un valor más de la pila:

format_string <- stack[0] offset <- 1 while (parsing): token = tokenize_one_more(format_string) if (needs_integer (token)): value <- stack[offset] offset = offset + 1 ...

Esto es, por supuesto, un pseudo-código muy incompleto que demuestra cómo la función tiene que depender de los argumentos pasados ​​para averiguar cuánto tiene que cargar y eliminar de la pila.

Seguridad

Esta dependencia de los argumentos proporcionados por el usuario también es uno de los mayores problemas de seguridad presentes (consulte https://cwe.mitre.org/top25/ ). Los usuarios pueden usar fácilmente una función variadic erróneamente, ya sea porque no leyeron la documentación, u olvidaron ajustar la cadena de formato o la lista de argumentos, o porque son completamente malos, o lo que sea. Vea también Format Attack String .

C Implementación

En C y C ++, las funciones variadas se usan junto con la interfaz va_list . Mientras que empujar hacia la pila es intrínseco a esos lenguajes ( en K + RC incluso podría reenviar-declarar una función sin indicar sus argumentos , pero aún así llamarla con cualquier número y amable argumento), la lectura desde una lista de argumentos desconocidos está interconectada a través de va_... -macros y va_list -type, que básicamente abstrae el acceso de bajo nivel de la estructura de la pila.

Sé que puedo usar va_arg para escribir mis propias funciones variadas, pero ¿cómo funcionan las funciones variadas bajo el capó, es decir, en el nivel de instrucción de ensamblaje?

Por ejemplo, ¿cómo es posible que printf tome una cantidad variable de argumentos?

* No hay regla sin excepción. No hay lenguaje C / C ++, sin embargo, esta pregunta puede ser respondida por ambos

* Nota: Respuesta originalmente dada a ¿Cómo puede la función printf tomar los parámetros variables en número mientras se los envía? , pero parece que no se aplicó al preguntador


Las funciones variables están definidas por el estándar, con muy pocas restricciones explícitas. Aquí hay un ejemplo, levantado de cplusplus.com.

/* va_start example */ #include <stdio.h> /* printf */ #include <stdarg.h> /* va_list, va_start, va_arg, va_end */ void PrintFloats (int n, ...) { int i; double val; printf ("Printing floats:"); va_list vl; va_start(vl,n); for (i=0;i<n;i++) { val=va_arg(vl,double); printf (" [%.2f]",val); } va_end(vl); printf ("/n"); } int main () { PrintFloats (3,3.14159,2.71828,1.41421); return 0; }

Las suposiciones son más o menos las siguientes.

  1. Debe haber (al menos uno) primer argumento fijo con nombre. El ... realidad no hace nada, excepto decirle al compilador que haga lo correcto.
  2. Los argumentos fijos proporcionan información sobre cuántos argumentos variados hay, por un mecanismo no especificado.
  3. Desde el argumento fijo, es posible que la macro va_start devuelva un objeto que permita recuperar los argumentos. El tipo es va_list .
  4. Desde el objeto va_list , es posible que va_arg itere sobre cada argumento variad y coaccione su valor en un tipo compatible.
  5. Algo extraño podría haber sucedido en va_start así que va_end hace las cosas bien otra vez.

En la situación más habitual basada en la pila, la va_list es simplemente un puntero a los argumentos que se encuentran en la pila, y va_arg incrementa el puntero, lo lanza y lo desreferencia a un valor. Entonces va_start inicializa ese puntero mediante una aritmética simple (y el conocimiento interno) y va_end no hace nada. No hay un lenguaje ensamblador extraño, solo un poco de conocimiento interno de dónde están las cosas en la pila. Lea las macros en los encabezados estándar para averiguar qué es eso.

Algunos compiladores (MSVC) requerirán una secuencia de llamada específica, mediante la cual la persona que llama liberará la pila en lugar del destinatario.

Funciones como printf funcionan exactamente así. El argumento fijo es una cadena de formato, que permite calcular la cantidad de argumentos.

Funciones como vsprintf pasan el objeto va_list como un tipo de argumento normal.

Si necesita más detalles o un nivel más bajo, por favor agregue a la pregunta.