En este artículo, discutiremos cómo funciona el argumento de longitud variable.
- función variádica
- Convención de llamadas
- Diseño de memoria del programa C/C++
- subíndice negativo
Función variadica: una función variadica son las plantillas que toman un argumento de longitud variable. Un argumento de longitud variable es una característica que permite que una función reciba cualquier número de argumentos. Hay situaciones en las que una función maneja un número variable de argumentos según los requisitos, como por ejemplo:
- Suma de números dados.
- Mínimo de números dados y muchos más.
Un número variable de argumentos están representados por tres puntos (…).
Programa 1:
C
// C program to demonstrate the use of // variable number of arguments #include <stdarg.h> #include <stdio.h> // Function to find the minimum of integer // numbers passed, ftrst argument is count // of numbers int min(int arg_count, ...) { int i; int min, a; // va_list is a type that holds the // information about variable arguments va_list ap; // va_start must be called before // accessing variable argument list va_start(ap, arg_count); // Now arguments can be accessed one // by one using va_arg macro. // Initialize min as the first // argument in list min = va_arg(ap, int); // Traverse the rest of arguments // to find out minimum for (i = 2; i <= arg_count; i++) if ((a = va_arg(ap, int)) < min) min = a; // va_end should be executed before // the function returns whenever // va_start has been previously // used in that function va_end(ap); return min; } // Driver Code int main() { int count = 5; printf("Minimum value is %d", min(count, 12, 67, 6, 7, 100)); return 0; }
Minimum value is 6
Convención de llamadas: la convención de llamadas se refiere a cómo se llama a una función, cómo se pasan los parámetros y cómo se limpia la pila. C / C++ tiene una variedad de convenciones de llamada, pero solo nos interesan __cdecl y __stdcall .
Ambos son muy similares pero tienen algunas diferencias.
- __cdecl es la convención de llamadas predeterminada de C/C++.
- __stdcall es la convención de llamada predeterminada para las funciones de la API de Windows .
Ahora ambas convenciones de llamada pasan parámetros de derecha a izquierda. Los desarrolladores de C/C++ eligen de derecha a izquierda en lugar de izquierda a derecha.
Diseño de la memoria: el diseño de la memoria se analiza a continuación:
Lo único que necesita atención es el segmento de pila y montón que crece en la dirección opuesta.
- El montón crece hacia una dirección superior.
- La pila crece hacia la dirección inferior. Significa que más alto en la pila es más bajo en la dirección. Cuando empujamos algo en la pila, obtiene la dirección inmediata más baja de la pila.
Entendamos esto por función:
Dentro de main() tenemos una función func(arg1, arg2, arg3)
Cuando se llama a func, main() se llama «persona que llama» y func() se llama «persona que llama» Veamos la variable local de la persona que llama
en la pila -> inferior en la pila más arriba en la dirección —- (otras cosas) arg3 (más a la derecha) arg2 arg1 —-
Variables locales de la persona a la que se llama -> superior en la pila inferior en la dirección ^ la nueva pila se crea aquí, no en la parte superior
En la sección anterior, puede ver claramente que el primer argumento obtiene la dirección más baja. Esta es la razón por la que los desarrolladores eligieron de derecha a izquierda en lugar de izquierda a derecha porque la convención de llamada de izquierda a derecha le dará al primer argumento la dirección más alta que puede causar un problema.
El primer argumento obtiene la dirección más baja y todos los parámetros tendrán una dirección continua en la pila.
Subíndice negativo: [] es el operador de subíndice. A continuación se presentan algunos de los puntos importantes a tener en cuenta sobre el operador de subíndice:
- Si el operador de subíndice está operando en un puntero, entonces su comportamiento es diferente. es decir, ptr[x] significa *(ptr + x), es decir, valor en x*sizeof(data_type_of_pointer) delante de ptr.
- De manera similar, ptr[-x] significa *(ptr – x), es decir, valor en x*sizeof(data_type_of_pointer) detrás de ptr.
A continuación se muestra un ejemplo para mostrar cómo funciona <stdarg.h>:
C++
// C++ program to implement stdarg.h #include <iostream> #include <stdarg.h> using namespace std; // Function to find the sum of numbers int sum(int num, ...) { int res = 0; va_list ap; va_start(ap, num); for (int i = 0; i < num; i++) { res += va_arg(ap, int); } // Return the resultant sum return res; } // Driver Code int main() { // First argument is the number // of arguments cout << sum(4, 6, 89, 34, 26); return 0; }
155
Explicación:
La implementación incorporada tiene algunas restricciones, echemos un vistazo a ellas y veamos cómo se pueden superar. Uno de ellos es que es necesario pasar el primer argumento como obligatorio, y veamos por qué es obligatorio y cómo evitarlo.
<stdarg.h> usa va_list, va_start, va_arg y va_end. El problema se puede dividir en 2 partes:
- ¿Qué hacen?
- ¿Cómo podemos replicarlo?
va_list: Es un typedef de char* pero esto es bastante diferente, ya que cuando se usa como un tipo de string C obtenemos resultados impredecibles. Esto se debe a que no es typedef común . Está integrado definido.
// arg.h typedef char* va_list;
va_start: Esta es una macro y lo que hace es inicializar ap, que en realidad es de tipo va_list ( char* ) con una dirección delante del primer parámetro, que es arg1 . Esta es la razón por la cual existe la necesidad de pasar el primer argumento como obligatorio. Puede ser cualquier valor de cualquier tipo de datos, pero por simplicidad, a menudo se pasa la cantidad de argumentos. Se utiliza para identificar la dirección en la pila donde los argumentos son continuos.
va_arg: Esta macro es bastante complicada. Hace dos cosas.
- Devuelve el parámetro requerido
- Avanza al siguiente parámetro
// arg.h
#define va_get(ap, tipo) ((tipo*)ap)// convierte la dirección mantenida por ap (aquí arg2) en tipo*
#define va_advance(ap, tipo) ap = ap + sizeof(tipo)
Veamos qué son estos tres puntos. En realidad, es un operador de eclipse ( . . . ) y está definido en C++. Este operador se utiliza para pasar un número variable de argumentos. Así es como funciona stdarg.h , si no puede usar el primer argumento en la función variádica. Podemos hacer uso de ella como
#define va_start(ap, arg1) (ap = (char*)(&arg1))
Sin embargo, esto no tiene sentido ya que no sabemos cuántos argumentos se pasan, entonces, ¿cómo podemos usarlos?
Publicación traducida automáticamente
Artículo escrito por mohdatishanx y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA